设计模式-适配器模式(结构型)、迭代器模式(行为型)、组合模式(结构型)

0 迭代器模式和适配器模式组合模式

这几个模式放在一起讲的原因在于:都在向外界提供统一的借口

适配器模式是在项目已经开始,新加入的对象需要和原有对象有一致的接口。

而迭代器模式应用则比较狭隘,只是为了遍历容器中的对象,向外提供统一的接口。

组合模式在于不同种类的某种事物向外提供统一的接口。

1 适配器模式

1.0 需求

通常都是某个类A已经在系统中使用了很长时间,使用该类的代码也很多。后来突然来了一个类B,类B具有和类A同样的功能,但是实现不同,而且向外提供的接口不同。也就是说类A和类B不派生自同一个类。

因此适配器模式解决的问题是,如何处理两个功能相同,但是提供接口的类,让其向外提供统一的接口。

方法很简单,新增一个类C,让类C关联(组合)类B,同时继承类A的基类。类C中的接口通过关联的类B实例实现一样的功能。

适配器模式的最终目标:已有的代码能够复用

要注意:之前的类A一定是一个派生类,也就是有一个基类。如果没有基类,这个模式行不通。如果之前的类没有一个基类。那么后面的类智能使用相同的方法调用。但是之前的代码不能够复用,因为需要的参数不能够转换。重载类型转换也是不可以的。

1.1 实现

class ABCompiler 
{
public:
   virtual void do()=0; 
};


// 已有的使用很久的类,不能够变更 class Gnu :public ABCompiler { public: void do(Path,Op);//选项和 do 方法结合在一起 }; // 新来了一个能够提供一样功能的类 class Clang { public: void compile(); void add_op(); // 编译选项需要额外添加 private: Op op; }; class CompilerClang :public ABCompiler { public: //内部实现和 gnu 相同的功能 virtual void do(Path path,Op op) { clang.add_op(op); clang.compile(Path); } private: Clang clang; //组合的方式,在 CompilerClang 创建的时候创建,销毁的时候销毁。 };

  

1.2 缺点

之前的类,一定要存在继承体系,不然已有的使用类的代码不能够工作。

也就是说要符号依赖倒置和里氏替换原则,才能够使用适配器模式。

2 迭代器模式

2.0 需求

存在不同的容器,vector和map,两者之间遍历元素的方式不同。一种是数组,一种是树。

而迭代器模式,就是封装两者遍历元素的行为,向外提供统一的接口:

  1. ++下一个元素
  2. --上一个元素

因此迭代器模式可以理解为重载容器的++和--运算符,但是直接重载容器类的++和--肯定不行,因为不能做到代码复用,也就是说使用vector的代码不能用在map上。

因此,新增一个Iterator的虚基类,有next、end和begin三个接口。然后为不同的容器新增一个具体的迭代器类,继承Iterator虚基类。

然后不同容器关联(组合)这个迭代器类的实例。

2.1 现状

迭代器模式,现在已经很少自己实现了。一般是有语言或是标准库提供。因为迭代器一般使用在容器上的。而容器又是标准库提供的。

c++标准库的迭代器也不是使用迭代器模式实现的。

而是使用模板+函数重载实现的。

 

3 组合模式

3.0 需求

在某些情况下,用到分支和节点。也就是子节点和叶节点,操作不同种类的节点之间出现的情况不同。

例如,linux下各种文件类型,打开普通文件和目录文件的结果是不同的。

因此需要一种设计模式,能够统一的访问这些资源。也就是说以统一的接口访问节点。

并且节点之间存在包含的关系。

这其实和适配器模式类似,都是需要提供统一的接口

3.1 实现

既然有统一的接口,那就需要一个基类提供接口,然后节点继承基类,覆写方法,实现多态。

节点之间存在包含的关系,类内关联一个容器,存储其子节点

//提供统一的方式
class Node
{
public:
  virtual void add(Node*)=0;
  virtual void remove(Node*)=0;
  virtual Node* show(int index)=0;
};

class left:public Node
{
//。。。。
private:
  vector<Node*> _list;
};

class Branch:public Node
{
//。。。
private:
  vector<Node*> _list;
};

  

如果基类提供节点所有可能的操作,而一个节点不应该包含某个操作,在编译的时候会报错,因为纯虚函数没有定义。

如果基类只提供各个节点都有的功能,不同类型的节点类自行添加方法,那么也是在编译期报错。

原文地址:https://www.cnblogs.com/perfy576/p/8549910.html