设计模式(1)——简单工厂模式和策略模式的结合应用
一、前言
设计,为蓝图,为构建之根本。个人对于设计模式的理解:设计模式是软件开发过程中一些常用pattern的概括总结。这些思想终究是别人实践过程中存在着背景因素的产物,可以用,但不可滥用。
最近在学习软件设计模式方面的知识,在这里实现每个使用场景记录一下自己的学习过程。
二、功能
简单工厂模式:是属于工厂模式的一种,它使用一个集中了所有实例(产品)的创建逻辑的工厂类去实例化对象。外界只需要告诉工厂所需生产产品的类型,而无需关注生产的过程。将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,降低类之间的耦合。
策略模式:策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合,其实就是用来封装变化。GOF里关于策略模式的功能描述——“策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能” “当不同的行为堆砌在同一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句”。
三、实例
以计算二维坐标空间两点间的不同类型距离(欧氏距离、曼哈顿距离和棋盘格距离)为例。
在基类定义一个抽象接口Calculate,在派生类中实现它。
1 // 距离计算基类 2 class DistanceBase 3 { 4 public: 5 virtual ~DistanceBase() = default; 6 public: 7 virtual double Calculate(cv::Point2d point1_, cv::Point2d point2_) = 0; 8 }; 9 10 // 欧几里得距离计算类 11 class EuclideanDistance : public DistanceBase 12 { 13 public: 14 double Calculate(cv::Point2d point1_, cv::Point2d point2_) 15 { 16 double result = 0.0; 17 result = pow((point1_.x - point2_.x), 2) + pow((point1_.y - point2_.y), 2); 18 result = sqrt(result); 19 20 return result; 21 } 22 }; 23 24 // 曼哈顿距离计算类 25 class ManhattanDistance : public DistanceBase 26 { 27 public: 28 double Calculate(cv::Point2d point1_, cv::Point2d point2_) 29 { 30 double result = 0.0; 31 result = abs(point1_.x - point2_.x) + abs(point1_.y - point2_.y); 32 33 return result; 34 } 35 }; 36 37 // 棋盘格距离计算类 38 class ChessboardDistance : public DistanceBase 39 { 40 public: 41 double Calculate(cv::Point2d point1_, cv::Point2d point2_) 42 { 43 double result = 0.0; 44 result = std::max(abs(point1_.x - point2_.x), abs(point1_.y - point2_.y)); 45 46 return result; 47 } 48 };
工厂类的实现
1 enum DistanceType 2 { 3 EUCLIDEAN = 0, 4 MANHATTAN = 1, 5 CHESSBOARD = 2, 6 }; 7 8 9 class DistanceFactory 10 { 11 public: 12 static DistanceBase* creatCalculator(DistanceType type) 13 { 14 DistanceBase* p = NULL; 15 switch (type) 16 { 17 case DistanceType::EUCLIDEAN: 18 p = new EuclideanDistance(); 19 break; 20 case DistanceType::MANHATTAN: 21 p = new ManhattanDistance(); 22 break; 23 case DistanceType::CHESSBOARD: 24 p = new ChessboardDistance(); 25 break; 26 default: 27 break; 28 } 29 30 return p; 31 } 32 };
Client端代码,根据输入的DistanceType来控制工厂类具体实例化哪个距离计算对象
DistanceBase* calculator = DistanceFactory::creatCalculator(DistanceType::EUCLIDEAN); double result = calculator->Calculate(cv::Point2d(1, 1), cv::Point2d(4, 5));
简单工厂模式的缺点除了违背了开放封闭原则以外,通过以上的Client端代码可以发现,Client需要认识DistanceBase基类和DistanceFactory工厂类两个类,可不可以让其只认识一个类?
这个时候可以结合策略模式来实现all in one的目的,使计算算法彻底地与客户端分离。代码如下
1 class DistanceCalculator 2 { 3 public: 4 DistanceCalculator(DistanceType type) 5 { 6 distance_ = NULL; 7 8 switch (type) 9 { 10 case DistanceType::EUCLIDEAN: 11 distance_ = new EuclideanDistance(); 12 break; 13 case DistanceType::MANHATTAN: 14 distance_ = new ManhattanDistance(); 15 break; 16 case DistanceType::CHESSBOARD: 17 distance_ = new ChessboardDistance(); 18 break; 19 default: 20 break; 21 } 22 } 23 24 double Calculate(cv::Point2d point1_, cv::Point2d point2_) 25 { 26 if (distance_) 27 return distance_->Calculate(point1_, point2_); 28 } 29 30 private: 31 DistanceBase* distance_; 32 };
Client端调用
DistanceCalculator* calculator = new DistanceCalculator(DistanceType::EUCLIDEAN); double result = calculator->Calculate(cv::Point2d(1, 1), cv::Point2d(4, 5));
DistanceBase类为一个抽象策略,而EuclideanDistance、ManhattanDistance和ChessboardDistance类为三个具体策略,将实例化具体策略的的过程转移到DistanceCalculator类中,客户端只需实例DistanceCalculator一个类,使得具体的计算算法与客户端彻底分离。
鄙人才疏学浅,如有错误还请指出。