学习完了简单工厂模式以后,感觉可以用于解决大多数多算法选择的问题了,接下来看一个新的例子,也是借用《大话数据结构》一书的例子,要实现的是一个商场促销的计算软件,输入商品的单价和数量,还有销售策略(如打折,返现),然后计算出总的价格。
首先可以从这个问题中抽象出两个算法类,一个是打折算法类,需要提供打折的幅度,如八折,另一个是返现算法类,需要提供返现的最小额度和返现金额,如满300减100,它们都有一个共同的方法,计算出总的价格,这里因为每个类里面只有一个计算总价的方法需要用到金额,所以可以直接把总金额作为函数的参数传入,而不用作为算法类的成员变量,用简单工厂模式实现如下:
1 class Collection 2 { 3 public: 4 virtual float GetSum(float money) { return money; } 5 virtual ~Collection() {} 6 }; 7 class CollectionDiscount :public Collection 8 { 9 private:10 float discount = 1.0f;11 public:12 CollectionDiscount() = default;13 CollectionDiscount(float d):discount(d){}14 float GetSum(float money) override15 {16 return money*discount;17 }18 };19 class CollectionReturn :public Collection20 {21 private:22 float limit=0.0f;23 float value = 0.0f;24 public:25 CollectionReturn() = default;26 CollectionReturn(float l,float v) :limit(l),value(v) {}27 float GetSum(float money) override28 {29 if (money >= limit)30 return money - value;31 else32 return money;33 }34 };35 class CollectionFactory36 {37 public:38 static shared_ptr<Collection> CreateCollection(string type)39 {40 shared_ptr<Collection> ptr = nullptr;41 if (type == "打八折")42 ptr = make_shared<CollectionDiscount>(0.8);43 else if (type == "满300送100")44 ptr= make_shared<CollectionReturn>(300, 100);45 else46 throw(invalid_argument("没有这种折扣策略"));47 return ptr;48 }49 };
1 int main()2 {3 shared_ptr<Collection> c = CollectionFactory::CreateCollection("打八折");4 cout << c->GetSum(100) << endl;5 return 0;6 }
这样工厂类的对象就可以通过输入的字符串来判断,并生成对应的算法类的对象,最后客户端就能调用算法类中的方法进行计算了,简单工程模式很好的解决的了对象的创建问题,但是有一个问题就是算法类的名称还是出现在了客户端程序中,那么对于大型商场而言,它的销售策略千奇百怪,总会出现很多新的销售策略算法,那么有没有一种模式,能完全不让算法类的名字出现在客户端程序。面对这种算法时常变动,可以采用策略模式。
策略模式就是定义了算法家族,将它们分别封装起来,让它们之间可以相互替换,可以让算法变化,但是不会影响到使用算法的客户。在这个例子里面前面两个算法类的内容都不需要修改,仅仅需要修改的就是将工厂类改为一个上下类,用上下文类来管理算法对象,同时这里为了将判断程序写在业务逻辑里面,所以将策略模式和简单工厂可以结合如下:
1 class Context//以对象管理算法资源 2 { 3 private: 4 Collection *pCollection=nullptr; 5 public: 6 Context() = default; 7 Context(string type) 8 { 9 if (type == "打八折")10 pCollection = new CollectionDiscount(0.8);11 else if (type == "满300返100")12 pCollection = new CollectionReturn(300, 100);13 else14 throw(invalid_argument("没有这种折扣策略"));15 }16 float ContextInterface(float money)17 {18 return pCollection->GetSum(money);19 }20 ~Context() { delete pCollection; }21 };
1 int main()2 {3 Context context("打八折");4 cout << context.ContextInterface(100) << endl;5 return 0;6 }
可以发现上下文类主要做的修改就是,它拥有一个指向一个算法对象的指针成员变量,也就是说,完全可以在这个类里面执行算法运算,然后再把结果传输给用户,然后因为指针是类的成员变量,所以可以很方便的在这个类的析构函数里面将这个指针所指对象销毁,就没没必要用到智能指针,也使得整个客户端代码简洁明了了不少,最后我们也可以神奇地发现客户端代码里面完全没有出现算法类,仅仅只出现了上下文类,使得算法与客户端完全分离了。
最后总结一下,策略模式定义了一系列算法的方法,所有算法都是完成了相同的工作,仅仅是实现不同,它可以通过上下文类以相同的方式调用所有的算法,最重要的一点就是减少了算法类和实用算法类之间的耦合。那什么时候用策略模式呢,策略模式就是用来封装算法的,但在实践中,可以用它来封装几乎任何类型的规则,主要在分析过程中听到需要不同时间,应用不同的业务规则,就可以考虑用策略模式处理这种变化的可能性。
最后一句,任何需求的变更都是需要成本的,整个程序员之路也是路漫漫其修远兮。
原标题:设计模式学习历程(二)————从简单工厂到策略模式
关键词:设计模式