设计模式(3)-策略模式(Strategy)

2024-03-12 17:48

本文主要是介绍设计模式(3)-策略模式(Strategy),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【描述】策略模式本质上利用的是面向对象的多态特性,构建者不必自身包含实现的逻辑,而是根据需要利用其他对象中的算法。

【UML图】

图1 UML图

(1) 定义了一个Methods基类,包含一个策略的接口MethodsInterface。

(2) 定义了MethodsStrategyA、MethodsStrategyB两种策略。

(3) 定义了一个策略构建者Context,包含ContextInterface方法。

 

【示例代码】

methods.h

#ifndef METHODS_H
#define METHODS_H
class Methods
{
public:
Methods();
public:
virtual void MethodsInterface();
};
#endif // METHODS_H

methods.cpp

#include <QDebug>
#include "methods.h"
Methods::Methods()
{
qDebug()<<"construct Methods";
}
void Methods::MethodsInterface()
{
qDebug()<<"Methods::MethodsInterface";
}

methodstrategya.h

#ifndef METHODSTRATEGYA_H
#define METHODSTRATEGYA_H
#include "methods.h"
class MethodStrategyA : public Methods
{
public:
MethodStrategyA();
public:
void MethodsInterface();
};
#endif // METHODSTRATEGYA_H

methodstrategya.cpp

#include <QDebug>
#include "methodstrategya.h"
MethodStrategyA::MethodStrategyA()
{
qDebug()<<"construct MethodStrategyA";
}
void MethodStrategyA::MethodsInterface()
{
qDebug()<<"Strategy A";
}

methodstrategyb.h

#ifndef METHODSTRATEGYB_H
#define METHODSTRATEGYB_H
#include "methods.h"
class MethodStrategyB : public Methods
{
public:
MethodStrategyB();
public:
void MethodsInterface();
};
#endif // METHODSTRATEGYB_H

methodstrategyb.cpp

#include <QDebug>
#include "methodstrategyb.h"
MethodStrategyB::MethodStrategyB()
{
qDebug()<<"construct MethodStrategyB";
}
void MethodStrategyB::MethodsInterface()
{
qDebug()<<"Strategy B";
}

context.h

#ifndef CONTEXT_H
#define CONTEXT_H
#include "methods.h"
class Context
{
public:
Context(Methods *method);
private:
Methods *_method;
public:
void ContextInterface();
};
#endif // CONTEXT_H

context.cpp

#include "context.h"
Context::Context(Methods *method)
{
_method = method;
}
void Context::ContextInterface()
{
_method->MethodsInterface();
}

main.cpp

#include <QDebug>
#include "context.h"
#include "methods.h"
#include "methodstrategya.h"
#include "methodstrategyb.h"
int main()
{
Context *context;
context = new Context(new MethodStrategyA);
context->ContextInterface();
context = new Context(new MethodStrategyB);
context->ContextInterface();
return 0;
}

 

【运行结果】

construct Methods 
construct MethodStrategyA 
Strategy A 
construct Methods 
construct MethodStrategyB 
Strategy B 


【结果分析】

利用多态特性,实现了不同策略的调用,由于是测试代码,本身很简单,省略了析构函数。

 

【实例剖析】

*本例来源于《重构-改善既有代码的设计》第1章。原例采用Java语言编写,本例利用Qt C++进行了改写。

看下面一个关于电影租赁计价的例子。

movie.h

#ifndef MOVIE_H
#define MOVIE_H
#include <QString>
enum {REGULAR = 0,NEW_RELEASE = 1,DISCOUNT = 2};
class Movie
{
public:
Movie();
Movie(QString title, int priceCode);
private:
QString _title;
int _priceCode;
public:
QString getTitle();
int getPriceCode();
void setPriceCode(int arg);
};
#endif // MOVIE_H

movie.cpp

#include <QDebug>
#include "movie.h"
Movie::Movie()
{
qDebug()<<"construct Movie::Movie()";
}
Movie::Movie(QString title, int priceCode)
{
qDebug()<<QString("construct Movie::Movie(%1,%2)").arg(title).arg(priceCode);
_title = title;
_priceCode = priceCode;
}
QString Movie::getTitle()
{
return _title;
}
int Movie::getPriceCode()
{
return _priceCode;
}
void Movie::setPriceCode(int arg)
{
_priceCode = arg;
}

rental.h

#ifndef RENTAL_H
#define RENTAL_H
#include "movie.h"
class Rental
{
public:
Rental(Movie movie, int daysRented);
private:
Movie _movie;
int _daysRented;
public:
Movie getMovie();
int getDaysRented();
double getCharge(int daysRented);
};
#endif // RENTAL_H

rental.cpp

#include <QDebug>
#include "rental.h"
Rental::Rental(Movie movie, int daysRented)
{
qDebug()<<"construct Rental";
_movie = movie;
_daysRented = daysRented;
}
Movie Rental::getMovie()
{
return _movie;
}
int Rental::getDaysRented()
{
return _daysRented;
}
double Rental::getCharge(int daysRented)
{
double result = 0;
qDebug()<<getMovie().getPriceCode();
switch(getMovie().getPriceCode())
{
case REGULAR:
result +=2;
if(daysRented>2)
{
result += (daysRented-2)*1.5;
}
break;
case NEW_RELEASE:
result += daysRented*3;
break;
case DISCOUNT:
result +=2;
if(daysRented>2)
{
result += (daysRented-2)*1;
}
break;
}
qDebug()<<result;
return result;
}


main.cpp

#include "movie.h"
#include "rental.h"
int main()
{
int daysRented = 30;
Movie movie("Book1",NEW_RELEASE);
Rental rental(movie,daysRented);
movie.setPriceCode(NEW_RELEASE);
rental.getCharge(daysRented);
return 0;
}


运行结果如下:

"construct Movie::Movie(Book1,1)" 
construct Movie::Movie() 
construct Rental 
1 
90 

示例调用了getCharge,并返回了priceCode相应的计价。相对于《重构》一书最原始代码,这段代码经过了一定的重构处理。

先看下述代码,

double Rental::getCharge(int daysRented)
{
double result = 0;
qDebug()<<getMovie().getPriceCode();
switch(getMovie().getPriceCode())
{
case REGULAR:
result +=2;
if(daysRented>2)
{
result += (daysRented-2)*1.5;
}
break;
case NEW_RELEASE:
result += daysRented*3;
break;
case DISCOUNT:
result +=2;
if(daysRented>2)
{
result += (daysRented-2)*1;
}
break;
}
qDebug()<<result;
return result;
}

这段代码,有一些不好的地方:

(1) 这段代码通过priceCode判断电影的租金,采用了switch-case语言,实质上还是面向过程的范畴,代码可重用性很低;

(2) 如果情形较多,switch-case会变得十分庞大。

 

有什么办法能去掉switch-case吗?下面通过引入策略模式实现这一目的。

先看UML图,

图2

(1) 引入了一个Price基类,提供了getCharge()接口供子类实现;

(2) 将switch-case语句中的3种情形抽取为3个Price子类;

(3) Movie调用了Price提供的接口。

 

【代码清单】

price.h

#ifndef PRICE_H
#define PRICE_H
class Price
{
public:
Price();
private:
int _priceCode;
public:
int getPriceCode();
void setPriceCode(int arg);
virtual double getCharge(int daysRented);
};
#endif // PRICE_H

price.cpp

#include <QDebug>
#include "price.h"
Price::Price()
{
qDebug()<<"construct Price";
_priceCode = 0;
}
int Price::getPriceCode()
{
qDebug()<<_priceCode;
return _priceCode;
}
void Price::setPriceCode(int arg)
{
_priceCode = arg;
}
double Price::getCharge(int daysRented)
{
qDebug()<<"Price::getCharge";
return daysRented;
}

regularprice.h

#ifndef REGULARPRICE_H
#define REGULARPRICE_H
#include "price.h"
class RegularPrice : public Price
{
public:
RegularPrice();
public:
double getCharge(int daysRented);
};
#endif // REGULARPRICE_H

regularprice.cpp

#include <QDebug>
#include "regularprice.h"
RegularPrice::RegularPrice()
{
qDebug()<<"construct RegularPrice";
}
double RegularPrice::getCharge(int daysRented)
{
qDebug()<<"RegularPrice::getCharge";
double result = 2;
if(daysRented>2)
{
result += (daysRented-2)*1.5;
}
return result;
}

newreleaseprice.h

#ifndef NEWRELEASEPRICE_H
#define NEWRELEASEPRICE_H
#include "price.h"
class NewReleasePrice : public Price
{
public:
NewReleasePrice();
public:
double getCharge(int daysRented);
};
#endif // NEWRELEASEPRICE_H

newreleaseprice.cpp

#include <QDebug>
#include "newreleaseprice.h"
NewReleasePrice::NewReleasePrice()
{
qDebug()<<"construct NewReleasePrice";
}
double NewReleasePrice::getCharge(int daysRented)
{
qDebug()<<"NewReleasePrice::getCharge";
return daysRented*3;;
}

discountprice.h

#ifndef DISCOUNTPRICE_H
#define DISCOUNTPRICE_H
#include "price.h"
class DiscountPrice : public Price
{
public:
DiscountPrice();
public:
double getCharge(int daysRented);
};
#endif // DISCOUNTPRICE_H

discountprice.cpp

#include <QDebug>
#include "discountprice.h"
DiscountPrice::DiscountPrice()
{
qDebug()<<"construct DiscountPrice";
}
double DiscountPrice::getCharge(int daysRented)
{
qDebug()<<"DiscountPrice::getCharge";
double result = 2;
if(daysRented>2)
{
result += daysRented;
}
return result;
}

movie.h

#ifndef MOVIE_H
#define MOVIE_H
#include <QString>
#include "price.h"
enum {REGULAR = 0,NEW_RELEASE = 1,DISCOUNT = 2};
class Movie
{
public:
Movie();
Movie(QString title, int priceCode);
~Movie();
private:
QString _title;
int _priceCode;
Price *_price;
public:
QString getTitle();
int getPriceCode();
void setPriceCode(int arg, Price *_price);
double getCharge(int daysRented);
};
#endif // MOVIE_H

movie.cpp

#include <QDebug>
#include "movie.h"
#include "regularprice.h"
#include "newreleaseprice.h"
#include "discountprice.h"
Movie::Movie()
{
qDebug()<<"construct Movie::Movie()";
}
Movie::Movie(QString title, int priceCode)
{
qDebug()<<QString("construct Movie::Movie(%1,%2)").arg(title).arg(priceCode);
_price = new Price;
_title = title;
_priceCode = priceCode;
}
Movie::~Movie()
{
delete _price;
}
QString Movie::getTitle()
{
return _title;
}
int Movie::getPriceCode()
{
return _price->getPriceCode();
}
void Movie::setPriceCode(int arg, Price *price)
{
_price = price;
_price->setPriceCode(arg);
}
double Movie::getCharge(int daysRented)
{
return _price->getCharge(daysRented);
}

rental.h

#ifndef RENTAL_H
#define RENTAL_H
#include "movie.h"
class Rental
{
public:
Rental(Movie movie, int daysRented);
private:
Movie _movie;
int _daysRented;
public:
Movie getMovie();
int getDaysRented();
double getCharge(int daysRented);
};
#endif // RENTAL_H

rental.cpp

#include <QDebug>
#include "rental.h"
Rental::Rental(Movie movie, int daysRented)
{
qDebug()<<"construct Rental";
_movie = movie;
_daysRented = daysRented;
}
Movie Rental::getMovie()
{
return _movie;
}
int Rental::getDaysRented()
{
return _daysRented;
}
double Rental::getCharge(int daysRented)
{
return _movie.getCharge(daysRented);
}

main.cpp

#include <QDebug>
#include "movie.h"
#include "rental.h"
#include "regularprice.h"
#include "newreleaseprice.h"
#include "discountprice.h"
int main()
{
int daysRented = 30;
Movie movie("Book1",NEW_RELEASE);
Rental rental(movie,daysRented);
movie.setPriceCode(NEW_RELEASE, new NewReleasePrice);
rental.getCharge(daysRented);
return 0;
}


【运行结果】

"construct Movie::Movie(Book1,1)" 
construct Price 
construct Movie::Movie() 
construct Rental 
construct Price 
construct NewReleasePrice 
NewReleasePrice::getCharge


【结果分析】

movie.setPriceCode(NEW_RELEASE, new NewReleasePrice);

传入priceCode和Price子对象作为参数,从运行结果可以看出,调用了NewReleasePrice的getCharge方法。

应用策略模式后,Rental::getCharge中仅有一行代码。

double Rental::getCharge(int daysRented)
{
return _movie.getCharge(daysRented);
}

将原来的getCharge做了搬移处理,而Movie::getCharge也很简短:

double Movie::getCharge(int daysRented)
{
return _price->getCharge(daysRented);
}


 

*应用策略模式后,消除了switch-case语句,函数更为简洁。将Price单独抽出,成为一个类组,提高了代码的可重用性。

 

【学习参考】

(1) 设计模式之三 --- 策略模式(Strategy Pattern)

 

【源码下载】

Qt设计模式1-8测试源码:http://download.csdn.net/detail/tandesir/4984275
 声明:该源码仅供学习交流,勿用于商业目的。


 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.csdn.net/tandesir





 


 

 

这篇关于设计模式(3)-策略模式(Strategy)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/802088

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停

软件架构模式:5 分钟阅读

原文: https://orkhanscience.medium.com/software-architecture-patterns-5-mins-read-e9e3c8eb47d2 软件架构模式:5 分钟阅读 当有人潜入软件工程世界时,有一天他需要学习软件架构模式的基础知识。当我刚接触编码时,我不知道从哪里获得简要介绍现有架构模式的资源,这样它就不会太详细和混乱,而是非常抽象和易

使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统