关键词:
前提:"单一职责"模式
在软件组件的设计中,如果责任划分的不清晰,使用继承,得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任
典型模式(表现最为突出)
装饰模式Decorator
桥接模式Bridge
一:装饰模式
(一)概念
装饰模式又叫做包装模式。通过一种对客户端透明的方式来扩展对象的功能,是继承关系的一个替换方案。
装饰模式就是把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择的,顺序的使用装饰功能包装对象
(二)动机
在某些情况下我们可能会“过度的使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;
并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多的子类膨胀。
即我们需要找到一种方式,实现功能可通过非继承方式扩展,但是接口不发生变化的类。
如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?
从而使得任何“功能扩展变化”所导致的影响将为最低?
(三)原代码讲解(流操作)
文件流,内存流,网络流,有对流进行加密,缓存等操作
//业务操作 class Stream //公共基类,含有共有方法 public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream() ; //主体类:文件流,网络流,内存流三个类 class FileStream: public Stream public: virtual char Read(int number) //读文件流 virtual void Seek(int position) //定位文件流 virtual void Write(char data) //写文件流 ; class NetworkStream :public Stream public: virtual char Read(int number) //读网络流 virtual void Seek(int position) //定位网络流 virtual void Write(char data) //写网络流 ; class MemoryStream :public Stream public: virtual char Read(int number) //读内存流 virtual void Seek(int position) //定位内存流 virtual void Write(char data) //写内存流 ; //扩展操作,对各个流进行操作,加密 class CryptoFileStream :public FileStream public: virtual char Read(int number) //额外的加密操作... FileStream::Read(number);//读文件流 静态特质,定死了,永远都是文件操作 virtual void Seek(int position) //额外的加密操作... FileStream::Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... FileStream::Write(data);//写文件流 //额外的加密操作... ; class CryptoNetworkStream : :public NetworkStream public: virtual char Read(int number) //额外的加密操作... NetworkStream::Read(number);//读网络流 virtual void Seek(int position) //额外的加密操作... NetworkStream::Seek(position);//定位网络流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... NetworkStream::Write(data);//写网络流 //额外的加密操作... ; class CryptoMemoryStream : public MemoryStream public: virtual char Read(int number) //额外的加密操作... MemoryStream::Read(number);//读内存流 virtual void Seek(int position) //额外的加密操作... MemoryStream::Seek(position);//定位内存流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... MemoryStream::Write(data);//写内存流 //额外的加密操作... ;
//扩展操作,对各个流进行操作,缓存操作 class BufferedFileStream : public FileStream //... ; class BufferedNetworkStream : public NetworkStream //... ; class BufferedMemoryStream : public MemoryStream //... //扩展操作,对各个流进行操作,加密和缓存组合操作,这里只写了一个,实际上有多种组合 class CryptoBufferedFileStream :public FileStream public: virtual char Read(int number) //额外的加密操作... //额外的缓冲操作... FileStream::Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... //额外的缓冲操作... FileStream::Seek(position);//定位文件流 //额外的加密操作... //额外的缓冲操作... virtual void Write(byte data) //额外的加密操作... //额外的缓冲操作... FileStream::Write(data);//写文件流 //额外的加密操作... //额外的缓冲操作... ; void Process() //编译时装配 CryptoFileStream *fs1 = new CryptoFileStream(); BufferedFileStream *fs2 = new BufferedFileStream(); CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
出现的问题:
需要的类的个数是: 1+n+n*(m+m-1+...+2+1) 其中n是我们流的个数,m是我们的操作类型个数。 我们上面的n=3,m=2所以需要的类是13个
问题的原因
除了其中的具体操作,例如:文件读取,网络读取等不同,我们发现所有的加密和缓存操作都是一样的方法,出现大量代码冗余,
(四)改进版本一(组合代替继承)
//扩展操作 class CryptoFileStream FileStream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写文件流 //额外的加密操作... ; class CryptoNetworkStream NetworkStream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读网络流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位网络流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写网络流 //额外的加密操作... ; class CryptoMemoryStream NetworkStream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读内存流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位内存流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写内存流 //额外的加密操作... ;
当一个变量的声明类型都是某个类的子类的时候,我们就该将他声明为某个类(基类),由于多态,我们可以使得他在未来(运行时)成为子类
//扩展操作 class CryptoFileStream Stream* stream; //使用基类,消除了编译时依赖 public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写文件流 //额外的加密操作... ; class CryptoNetworkStream Stream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读网络流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位网络流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写网络流 //额外的加密操作... ; class CryptoMemoryStream Stream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读内存流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位内存流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写内存流 //额外的加密操作... ;
我们发现这3个类一样,除了类名之外都相同,所以我们可以进行合并,消除重复性
//扩展操作 class CryptoFileStream Stream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写文件流 //额外的加密操作... ; class BufferedFileStream //所有相同操作都将去重,只保留一个 //... ;
同时发现一个问题,我们从继承转组合以后的虚函数哪来的?我们还是要遵循留的规范,即基类为我们设置的接口虚函数。我们还是需要进行继承,完善接口规范,不过只需要继承流的基类即可
//扩展操作 class CryptoStream :public Stream Stream* stream; public: virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... stream->Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream->Write(data);//写文件流 //额外的加密操作... ; class BufferedStream : :public Stream //... ;
下面是全部修改代码
//业务操作 class Stream public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream() ; //主体类 class FileStream: public Stream public: virtual char Read(int number) //读文件流 virtual void Seek(int position) //定位文件流 virtual void Write(char data) //写文件流 ; class NetworkStream :public Stream public: virtual char Read(int number) //读网络流 virtual void Seek(int position) //定位网络流 virtual void Write(char data) //写网络流 ; class MemoryStream :public Stream public: virtual char Read(int number) //读内存流 virtual void Seek(int position) //定位内存流 virtual void Write(char data) //写内存流 ; //扩展操作 class CryptoStream: public Stream Stream* stream;//... public: CryptoStream(Stream* stm):stream(stm) virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 //动态特质:由组合实现 virtual void Seek(int position) //额外的加密操作... stream::Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream::Write(data);//写文件流 //额外的加密操作... ; class BufferedStream : public Stream Stream* stream;//... public: BufferedStream(Stream* stm):stream(stm) //... ; void Process() //运行时装配 FileStream* s1=new FileStream(); CryptoStream* s2=new CryptoStream(s1); BufferedStream* s3=new BufferedStream(s1); //上面对s2加密了,我们再进行s2的缓存,实现了既加密又缓存 BufferedStream* s4=new BufferedStream(s2);
运行时装配:
我们编译时不存在文件加密,网络加密,文件缓存,文件加密缓存等操作,我们在运行时对其进行组合装配起来满足我们的需求
另外注意:
根据重构中所说:当我们类中含有重复字段和方法,我们应该将其提到前面基类中去,这里我们若是将Stream* stream提到Stream基类中去,会发现在主体类中会包含这个不需要的字段,所以这个时候我们应该设计一个中间基类。这时就引用出来了装饰模式
(五)改进版本二(使用装饰模式<中间基类>)
//扩展操作 //中间类 class DecoratorStream: public Stream protected: Stream* stream; public: DecoratorStream(Stream* stm):stream(stm) ; class CryptoStream: public DecoratorStream public: CryptoStream(Stream* stm):DecoratorStream(stm) virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... stream::Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream::Write(data);//写文件流 //额外的加密操作... ; class BufferedStream : public DecoratorStream public: BufferedStream(Stream* stm):DecoratorStream(stm) //... ;
最终规模是:1+n+1+m
全部代码
//业务操作 class Stream public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream() ; //主体类 class FileStream: public Stream public: virtual char Read(int number) //读文件流 virtual void Seek(int position) //定位文件流 virtual void Write(char data) //写文件流 ; class NetworkStream :public Stream public: virtual char Read(int number) //读网络流 virtual void Seek(int position) //定位网络流 virtual void Write(char data) //写网络流 ; class MemoryStream :public Stream public: virtual char Read(int number) //读内存流 virtual void Seek(int position) //定位内存流 virtual void Write(char data) //写内存流 ; //扩展操作 DecoratorStream: public Stream protected: Stream* stream;//...核心 DecoratorStream(Stream * stm):stream(stm) ; class CryptoStream: public DecoratorStream public: CryptoStream(Stream* stm):DecoratorStream(stm) virtual char Read(int number) //额外的加密操作... stream->Read(number);//读文件流 virtual void Seek(int position) //额外的加密操作... stream::Seek(position);//定位文件流 //额外的加密操作... virtual void Write(byte data) //额外的加密操作... stream::Write(data);//写文件流 //额外的加密操作... ; class BufferedStream : public DecoratorStream public: BufferedStream(Stream* stm):DecoratorStream(stm) //... ; void Process() //运行时装配 FileStream* s1=new FileStream(); CryptoStream* s2=new CryptoStream(s1); BufferedStream* s3=new BufferedStream(s1); BufferedStream* s4=new BufferedStream(s2);
以组合的方式来支持未来多态的变化
(六)模式定义
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。 ——《设计模式》GoF
(七)类图(结构)
(八)要点总结
1.通过采用组合而非继承的手法, Decorator模式实现了在运行时 动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又 表现为has-a Component的组合关系,即Decorator类又使用了 另外一个Component类。
DecoratorStream: public Stream protected: Stream* stream;//... DecoratorStream(Stream * stm):stream(stm) ;
若是程序中一个类的父类和他的字段类是同一个类的,那么他有极大的几率是Decorator模式。因为一般继承就不组合,组合就不继承
而我们这里的继承是为了接口的规范,组合是为了将来支持具体实现类
3.Decorator模式的目的并非解决“多子类衍生的多继承”问题, Decorator模式应用的要点在于解决“主体类在多个方向上的扩展 功能”——是为“装饰”的含义。
主体操作和扩展操作应该分开分之继承
(九)案例实现(装饰车)
//业务规范 class Car public: virtual void show() = 0; virtual ~Car() ;
//主体类 class RunCar :public Car public: virtual void show() cout << "running" << endl; ; class SwimCar :public Car public: virtual void show() cout << "swimming" << endl; ; class FlyCar :public Car public: virtual void show() cout << "flying" << endl; ;
//装饰中间类 class DecoratorCar : public Car protected: Car * car; public: DecoratorCar(Car* c) :car(c) ;
//扩展操作类 class EquipEngine :public DecoratorCar public: EquipEngine(Car* c) :DecoratorCar(c) virtual void show() cout << "equip engine can run fast" << endl; car->show(); ; class EquipWing :public DecoratorCar public: EquipWing(Car* c) :DecoratorCar(c) virtual void show() cout << "equip wing can fly" << endl; car->show(); ; class EquipPaddle :public DecoratorCar public: EquipPaddle(Car* c) :DecoratorCar(c) virtual void show() cout << "equip paddle can swing" << endl; car->show(); ;
int main() SwimCar* car = new SwimCar(); EquipPaddle *scar = new EquipPaddle(car); EquipEngine *ecar = new EquipEngine(scar); EquipWing *wcar = new EquipWing(ecar); wcar->show(); system("pause"); return 0;
mybatis框架之装饰模式
...装饰模式,或许可以说是变异版本。其实,我觉得大多的设计模式无非就是面向接口编程与接口 查看详情
设计模式原则之:单一职责原则srp
...扩展性才能提高。面向对象的软件设计中,有23种经典的设计模式,是一套前人代码设计经验的总结,如果把设计模式比作武功招式,那么设计原则就好比是内功心法。常用的设计原则有七个,本文将具体介绍单一职责原则。设... 查看详情
设计模式面向对象设计原则之单一职责原则
引用自:http://blog.csdn.net/lovelion 作者:刘伟 单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小。单一职责原则定义如下:单一职责原则(SingleResponsibilityPrinciple,SRP):一个类只负责一个功能领域中的相... 查看详情
设计模式之单一职责原则
demo(反面教程): 摩托车、汽车时没有问题的,但是飞机在公里运行,即不合理。因为类中的run方法违反了单一职责原则。 改进: 以上其实是方法级别上遵守单一职责原则。另外一种方法是... 查看详情
设计模式入门之装饰器模式decorator
//装饰模式定义:动态地给一个对象加入一些额外的职责。//就添加功能来说,装饰模式比生成子类更为灵活//这也提现了面向对象设计中的一条基本原则,即:尽量使用对象组合。而不是对象继承//Component:组件对象的接口。能... 查看详情
设计模式(六大原则之单一职责)
概念:就一个类而言,应该仅有一个引起它变化的原因 描述的意思是每个类都只负责单一的功能,切不可太多,并且一个类应当尽量的把一个功能做到极致。如果一个类承担的职责过多,就等于把这些职责耦合在一起,这种... 查看详情
设计模式(代码片段)
设计原则装饰模式动态代理命令模式工厂模式策略模式委派模式设计原则开闭原则、里氏替换原则、依赖倒转原则、接口隔离原则、最少知道原则、单一职责原则、合成复用原则开闭原则对修改关闭,对扩展开发。里氏替换原则... 查看详情
西游记之设计模式原则——单一职责原则
单一职责原则——专心致志只做一件事1packagedanyizhize;23classSunWuKong{4publicvoidXiangMo(Stringname){5System.out.println(name+"降魔!");6}78publicvoidXingLi(Stringname){9System.out.println(name+"拿行李!");10}1112publicvoidQ 查看详情
大话设计模式第三章之单一职责原则
...序员 叫他去做医学研究,生物研究,可能会抑制他学设计模式的能力)。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到异常不到的破坏(你医学研究久了, 查看详情
「设计模式」六大原则之一:单一职责小结(代码片段)
...6.应用示例18应用示例2(结合组合模式)9.小结「设计模式」六大原则系列链接:「设计模式」六大原则之一:单一职责小结「设计模式」六大原则之二:开闭职责小结「设计模式」六大原则之三:里氏替换原则... 查看详情
23种设计模式之单一职责原则(代码片段)
单一职责原则基本介绍对类来说,即一个类应该只负责一项职责,如类A负责两个不同的职责:职责1,职责2。当职责1的需求变更而更改A时,可能造成职责2执行错误,所以要将类A的粒度分解为A1,A2两... 查看详情
设计模式之装饰模式(代码片段)
装饰模式一:装饰模式概述装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,比如把房子装修就是一个很典型的场景。装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动... 查看详情
设计模式之单一职责原则(srp)
自己之前写过一些关于设计模式的博客,但是大部分都写得比较匆忙。现在正好趁年前有时间,笔者打算好好地整理一下自己这块知识结构。开篇的第一个原则就是设计原则里面最简单的一个原则--单一职责原则。&... 查看详情
设计模式之禅第1章单一职责原则
1.1我是“牛”类,我可以担任多职吗SRP 单一职责原则的英文名称是SingleResponsibilityPrinciple,简称是SRP。RBAC模型(Role-BasedAccessControl)基于角色的访问控制 通过分配和取消... 查看详情
设计模式学习笔记设计基本原则之单一职责原则
单一职责原则(SRP:SingleResponsibilityPrinciple)名词解释:1) 职责:是指类变化的原因。2) 职责扩散:就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。3)可变类:是指创建该类的实例后,可以对其属性进行修... 查看详情
设计模式-六大设计原则之srp(单一职责)(代码片段)
文章目录概述CaseBadImplBetterImpl1.定义接口2.职责分离-多种实现类3.单元测试小结概述单一职责原则(SingleResponsibilityPrinciple,SRP)又称单一功能原则,是面向对象的五个基本原则(SOLID)之一。它规定一个类... 查看详情
设计模式-六大设计原则之srp(单一职责)(代码片段)
文章目录概述CaseBadImplBetterImpl1.定义接口2.职责分离-多种实现类3.单元测试小结概述单一职责原则(SingleResponsibilityPrinciple,SRP)又称单一功能原则,是面向对象的五个基本原则(SOLID)之一。它规定一个类... 查看详情
设计模式之装饰模式
装饰模式:动态地给一个对象添加一些额外的职责。如果是增加功能,装饰模式比生成子类更灵活。 1.普通的手机publicinterfacePhone{publicvoidcall();}publicclassMyPhoneimplementsPhone{@Overridepublicvoidcall(){System.out.println("打电话!");}}2.装饰... 查看详情