课程目标
1.理解松耦合设计思想
2.掌握面向对象设计原则
3.掌握重构技法改善设计
4.掌握GOF核心设计模式
设计模式
描述重复发生的问题以及该问题解决方案的核心。
面向对象OOP
向下:
封装——隐藏内部实现
继承——复用现有代码
多态——改写对象行为
向上:将真实世界,抽象为程序代码(好厉害的样子,黑客帝国?-?)
软件设计很复杂,有人为原因,也有代码原因。
解决方案:
分解——大----小;复杂---简单。
抽象——保留本质,删除细节,模型化。
软件设计的目标:复用
重新认识OOP
1.理解隔离变化
架构的系统能适应新变化,将变化范围减到最小。
2.各司其职
新增类型不应该影响原来类型的实现。
3.对象的含义
语言实现-----对象即封装的代码和数据
规格层面-----对象即一系列可被使用的公共接口
概念层面-----对象即拥有某种责任的抽象
面向对象设计原则(抵御变化,将变化范围降到最小)加班少~~
1.依赖倒置原则(DIP)
2.开放封闭原则(OCP)
3.单一职责原则(SRP)
4.Liskov替换原则(LSP)
·子类必须能够替换他们的基类(IS-A)。
·继承表达类型抽象。
5.接口隔离原则(ISP)
·接口小 而完备。
·不强迫客户程序依赖他们不用的方法(简单直接)
6.优先使用对象组合,而不是类继承
7.封装变化点
8.针对接口编程,而不是针对实现编程
·将变量类型声明为某个接口
·客户程序只需知道对象具有的接口
·减少系统中各部分的依赖关系,实现“高内聚,松耦合”的类型设计方案
面向接口设计(接口标准化)
GOF-23模式分类
目的分类:
---------------------创建型模式-----------------------
单例模式:保证创建的一个对象(类)只有一个例。
原型模式:要创建和原型相同的新对象
工厂方法:用于创建对象的接口,来让子类决定实例化哪一个类。
抽象工厂:提供一个创建一系列或相关依赖对象的接口,而无需指定它们具体的类。
建造者:创建对象时,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
--------------------结构型模式------------------------
适配器:将一个类的接口转换成客户希望的另外一个接口。
外观:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
代理:为其他对象提供一种代理以控制对这个对象的访问。
装饰:动态的给一个对象添加一些额外的职责。
享元:运用共享技术,有效的支持大量细粒度的对象。
组合:将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性
桥接:将抽象部分与它的实现部分分离,使他们独立变化。
----------------------行为型模式--------------------
观察者:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
模板:定义一个操作的算法骨架,而将一些步骤延迟到子类中,模板方法是的子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
命令:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;可以对请求排队或记录请求日志,以及支持可撤销的操作。
状态:允许一个对象再起内部状态改变时改变他的行为,让对象看起来似乎修改了它的类。
职责链:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连城一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
解释器:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
中介者:用一个中介对象来封装一些列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合,而且可以独立的改变它们之间的交互。
访问者:表示一个作用于某对象结构中的各元素的操作。它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作。
策略:定义一些列的算法,把他们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。
备忘录:再不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态。这样以后就可将该对象恢复到原先保存的状态。
迭代器:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。
范围分类
----------------------类模式(处理类与子类)--------静态关系----------------------
具体未知…………(以后补充)
----------------------对象模式(处理对象)-----------动态关系----------------------
具体未知…………(以后补充)
封装变化分类
---------------------组件协作--------------------
模板:Template Method
策略:Strategy
观察者/事件:Observer/Event
---------------------单一职责--------------------
装饰:Decorator
桥接:Bridge
---------------------对象创建--------------------
工厂:Factory Method
抽象工厂:Abstract Factory
原型:Prototype
建造者:Builder
---------------------对象性能--------------------
单例:Singleton
享元:Flyweight
---------------------接口隔离--------------------
外观:Facade
代理:Proxy
中介者:Mediator
适配器:Adapter
---------------------状态变化--------------------
备忘录:Memento
状态:State
---------------------数据结构--------------------
组合:Composite
迭代器:Iterator
职责链:Chain of Responsibility
---------------------行为变化--------------------
命令:Command
访问者:Visitor
---------------------领域问题--------------------
解释器:Interpreter
重构关键技法
静态---------->动态
早绑定------->晚绑定
继承---------->组合
编译时依赖-->运行时依赖
紧耦合------->松耦合
——————————————————————————————————————————————————
模板:Template Method
定义:
一个操作的算法骨架,而将一些步骤延迟到子类中,模板方法是的子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
举例:把动物放在冰箱的步骤1、打开冰箱门,2、放进去,3、关上冰箱门。(Template Method)但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现,重现特定步骤);
代码演示:
//程序库开发人员
//template_lib1.cpp
class Library{
public:
void Step1(){
//...
}
void Step3(){
//...
}
void Step5(){
//...
}
};
//应用程序开发人员
//template_app1.cpp
class Application{
public:
bool Step2(){
//...
}
void Step4(){
//...
}
};
int main()
{
Library lib();
Application app();
lib.Step1();
if (app.Step2()){
lib.Step3();
}
for (int i = 0; i < 4; i++){
app.Step4();
}
lib.Step5();
}
//程序库开发人员
//template_lib1.cpp
class Library{
public:
//稳定 template method
//稳定中有变化,很多设计模式的代码结构
void Run(){
Step1();
if (Step2()) { //支持变化 ==> 虚函数的多态调用
Step3();
}
for (int i = 0; i < 4; i++){
Step4(); //支持变化 ==> 虚函数的多态调用
}
Step5();
}
virtual ~Library(){ }
protected:
void Step1() { //稳定
//.....
}
void Step3() {//稳定
//.....
}
void Step5() { //稳定
//.....
}
virtual bool Step2() = 0;//变化
virtual void Step4() =0; //变化
};
//应用程序开发人员
//template_app1.cpp
class Application : public Library {
protected:
virtual bool Step2(){
//... 子类重写实现
}
virtual void Step4() {
//... 子类重写实现
}
};
int main()
{
Library* pLib=new Application();
lib->Run();
delete pLib;
}
}
要点总结
1.非常基础的设计模式,在OOP中被被大量应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点。是代码复用方面的基本实现结构。
2.除了可灵活应对子步骤变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用
3.在具体实现方面,被Template Method调用的虚方法可以有具体实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将他们设置为protected.。
策略:Strategy
动机:
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用或不常使用的算法也是一个性能负担。
如何在运行时根据需要透明地改变对象的算法?将算法和对象本身解耦,从而避免上述问题?
定义:
定义一系列算法, 把它们一个个封装起来,并且使它们可以互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
第一种方法:
如果使用第一种方法,需要更改枚举类型和if else语句(第6行和24行),违背了开放封闭原则(对扩展开发,对修改封闭);
// strategy1.cpp
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //更改
};
class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if (tax == CN_Tax){
//CN***********
}
else if (tax == US_Tax){
//US***********
}
else if (tax == DE_Tax){
//DE***********
}
else if (tax == FR_Tax){ //更改
//...
}
//....
}
第二种方法:
采用第二种方法(策略模式),定义税法基类,对不同国家税法定义不同子类,override Calculate方法。59行,多态调用。
假设出现变化:扩展32 - 39行,SalesOrder不变(复用性,遵循开放封闭原则)。
补:面向对象提到的复用性,指的是编译单位(二进制单位)的复用性,不是粘贴代码的复用性
//Strategy.cpp
class TaxStrategy{
public:
virtual double Calculate(const Context& context)=0;
virtual ~TaxStrategy(){}
};
class CNTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
class USTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
class DETax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
//扩展
//*********************************
class FRTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//.........
}
};
class SalesOrder{
private:
TaxStrategy* strategy;
public:
SalesOrder(StrategyFactory* strategyFactory){
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder(){
delete this->strategy;
}
public double CalculateTax(){
//...
Context context();
double val =
strategy->Calculate(context); //多态调用
//...
}
};
要点总结:
Strategy及其子类为组件提供了一系列可重用的算法,从而可以得到类型在运行时方便地根据需要在各个算法之间进行切换。
Strtegy模式提供了用条件判断语句以外的另一种选择消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。(除非if else语句绝对不变,如对一周七天判断等)
如果Strategy对象没有实例变量,那么各个上下午可以共享同一个Strategy对象,从而节省对象开销。
观察者/事件:Observer/Event
定义:
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
第一种方法:
分析代码:违背依赖倒置原则
第6行: ProgressBar作为实现细节(表现形式可以多样变化,如图形界面,数字显示等)。其扮演的角色和任务是通知。
将通知的任务不用控件方式(太过细节)实现。
//FileSplitter1.cpp
class FileSplitter
{
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar;
public:
FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
m_filePath(filePath),
m_fileNumber(fileNumber),
m_progressBar(progressBar){
}
void split(){
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
m_progressBar->setValue(progressValue);
}
}
// MainForm1.cpp
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
FileSplitter splitter(filePath, number, progressBar);
splitter.split();
}
};
第二种方法:(重构第一种)
添加 IProgress类,作为抽象的通知机制,即观察者(订阅者)。观察者内含有更新方法DoProgress()。具体观察者(MainForm/ConsoleNotifier)继承该类,实现各自更新功能。
FileSplitter作为被观察对象(对象/发布者),其中含有抽象通知机制IProgress指针,利用DoProgress()方法进行通知。
即对象(发布者)不必考虑观察者情况,自动进行通知(发布)工作;
观察者(订阅者)根据自身实际情况选择是否订阅或如何处理通知。
同时考虑多个观察者问题添加List
//FileSplitter2.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
class FileSplitter
{
string m_filePath;
int m_fileNumber;
List m_iprogressList; // 抽象通知机制,支持多个观察者
public:
FileSplitter(const string& filePath, int fileNumber) :
m_filePath(filePath),
m_fileNumber(fileNumber){
}
void split(){
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);//发送通知
}
}
void addIProgress(IProgress* iprogress){
m_iprogressList.push_back(iprogress);
}
void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
protected:
virtual void onProgress(float value){
List::iterator itor=m_iprogressList.begin();
while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value); //更新进度条
itor++;
}
}
};
//MainForm2.cpp
class MainForm : public Form, public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
ConsoleNotifier cn;
FileSplitter splitter(filePath, number);
splitter.addIProgress(this); //订阅通知
splitter.addIProgress(&cn); //订阅通知
splitter.split();
splitter.removeIProgress(this);
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
Observer: IProgress; Update():DoProgress() .
ConcreteSubject: FileSplitter
ConcreteObserver: Mainform / ConsoleNotifier
根据标准的Observer模式定义,也可将addIProgress,removeProgress,onProgress单独定义类(Subject),再将FileSplitter(ConcreteSubject)继承此类。
上述方法2 相当于将类图中的Subject 和 ConcreteSubject合二为一。
要点总结:
1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。
2.目标发送通知时,无需指定观察者,通知会(可以携带通知信息作为参数)自动传播。
onProgress(progressValue);//发送通知,无需考虑具体的观察者问题。
3.观察者自己决定是否订阅通知,目标对象对此一无所知。
79 splitter.addIProgress(this); //订阅通知
80 splitter.addIProgress(&cn); //订阅通知
4.Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。
---------------------单一职责--------------------
在软件组件设计中,如果责任划分的不清晰,使用继承得到的结果,往往会随着需求变化,子类急剧膨胀,同时伴随代码重复。这个时候最重要的是划清责任。
装饰:Decorator
桥接:Bridge
---------------------
装饰:Decorator
动机:
某些情况我们可能会过度使用继承来扩展对象功能,由于继承为类型引入了静态特质,使得这种扩展方式缺乏灵活性,并且随着子类(扩展功能)的增多,各种子类(扩展功能)的组合会导致更多子类的膨胀。
如何使(对象功能的扩展)根据需要动态实现?
同时避免(扩展功能的增多)带来的子类膨胀问题?
将任何(功能扩展变化)带来的影响降为最低?————————装饰模式:Decorator
定义:
动态(组合)的给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码,减少子类个数。)
结构:
举例:
比如有一个手机,允许你为手机添加特性,比如增加挂件、屏幕贴膜等。一种灵活的设计方式是, 将手机嵌入到另一对象中,由这个对象完成特性的添加,我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。
要点总结
1.通过采用组合并非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的”灵活性差“和”多子类衍生问题
2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
3.Decorator模式的目的并非解决”多字类衍生的多继承“问题,Decorator模式应用的要点在于解决”主体类在多个方向上的扩展功能“(显然file,network与加密,缓冲是两种扩展方向) ——是为”装饰“的含义。
代码示例:
不同的流操作(文件流,网络流,内存流)及其扩展功能(加密,缓冲)等的实现
代码1
数据规模: 假设有n种文件,m种功能操作。该实现方法有(1 + n + n * m! / 2) 数量级的子类;
同时考察59行,79行,98行本身是相同的代码(类似还有很多),存在大量的冗余和重复。
开始重构,见方法2.
//Decorator1.cpp
//业务操作
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();
}
代码2
针对上述代码,重构步骤如下:
1.考察 CryptoFileStream ,CryptoNetworkStream,CryptoMemoryStream三个类,将其继承FileStream,NetworkStream,NetworkStream改为组合;即
class CryptoFileStream{
FileStream* stream;
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
// ...seek() write() 同理
}
}
class CryptoNetworkStream{
NetworkStream* stream;
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
//... seek() write() 同理
}
}
class CryptoMemoryStream{
MemoryStream* stream;
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
//... seek() write() 同理
}
}
2.考察上述2行, 13行, 24行, 发现其均为Stream子类, 应使用多态性继续重构。
class CryptoFileStream{
Stream* stream; // = new FileStream()
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
// ...seek() write() 同理
}
}
class CryptoNetworkStream{
Stream* stream; // = new NetworkStream();
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
//... seek() write() 同理
}
}
class CryptoMemoryStream{
Stream* stream; // = newMemoryStream()
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
//... seek() write() 同理
}
}
3.发现三个类是相同的,不同的实现(需求的变化)是在运行时实现,编译时复用,改为一个类即可,命名为CryptoStream。
同时为了保证接口规范(read,seek等仍然是虚函数),继承Stream,出现既有组合,又有继承的情况。
class CryptoStream : public Stream{
Stream* stream; // = new ...
public:
virtual char Read(int number){
//额外的加密操作...
stream -> Read(number);//改用字段方式调用Read()
// ...seek() write() 同理
}
}
4.添加相应构造器,得到此轮重构后的结果,代码如下,主要查看使用方式(运行时装配):
//Decorator2.cpp
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);
BufferedStream* s4=new BufferedStream(s2);
}
代码3
上述实现代码2已经极大地缓解了冗余问题,符合面向对象的设计思想,该轮重构是锦上添花。
重构步骤如下:
考察上述代码,多个子类都有同样的字段(Stream* stream;//...)
应考虑“往上提”,方法有两种,第一种是提到基类(显然不合适,FileStream等并不需要Stream字段 )
所以考虑第二种方法,实现一个“中间类”。
DecoratorStream: public Stream{
protected:
Stream* stream;//...
DecoratorStream(Stream * stm):stream(stm){
}
};
CryptoStream等继承中间类DecoratorStream:
class CryptoStream: public DecoratorStream {
public:
CryptoStream(Stream* stm):DecoratorStream(stm){
}
//...
}
重构完成的最终版本:
FileStream,NetworkStream,MemoryStream等可以创建各自的对象;
但实现加密,缓存功能必须在已有FileStream/NetworkStream等对象基础上;
这些操作本质是扩展操作,也就是“装饰”的含义。
此时类图示意:
这时类的数量为(1 + n + 1 + m)
//Decorator3.cpp
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{
Stream* stream;//...
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);
}
桥接:Bridge
动机:
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个变化的维度。
如何应对“多种维度变化”?利用OOP技术使类型可以轻松沿着2个乃至多个方向变化,而不引入额外的复杂度?
定义:
将抽象部分(业务功能)与实现部分(平台实现)分离,使他们都可以独立地变化。
结构:
要点总结:
1.Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象的实现可以沿着各自的维度来变化。所谓抽象和实现研制各自维度的变化,即“子类化”他们。
2.Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个雷只有一个变化的原因),复用性较差。Bridge模式是比多继承更好的解决方案。
3.Bridge模式的应用一般在“两个非常强的变化维度”有时一个类也有多余两个的变化维度,这是可以使用Bridge的扩展模式。
代码示例:
实现一个Messager,含有基本功能PlaySound,Connect等,并有PC、Mobile不同的平台实现 和 精简、完美等不同业务功能的版本
方法1
Bridge1.cpp
类的个数:1 + n + m*n,数量巨大且不同类之中有大量重复
class Messager{
public:
virtual void Login(string username, string password)=0;
virtual void SendMessage(string message)=0;
virtual void SendPicture(Image image)=0;
virtual void PlaySound()=0;
virtual void DrawShape()=0;
virtual void WriteText()=0;
virtual void Connect()=0;
virtual ~Messager(){}
};
//平台实现 n
class PCMessagerBase : public Messager{
public:
virtual void PlaySound(){
//**********
}
virtual void DrawShape(){
//**********
}
virtual void WriteText(){
//**********
}
virtual void Connect(){
//**********
}
};
class MobileMessagerBase : public Messager{
public:
virtual void PlaySound(){
//==========
}
virtual void DrawShape(){
//==========
}
virtual void WriteText(){
//==========
}
virtual void Connect(){
//==========
}
};
//业务抽象 m
class PCMessagerLite : public PCMessagerBase {
public:
virtual void Login(string username, string password){
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
PCMessagerBase::DrawShape();
//........
}
};
class PCMessagerPerfect : public PCMessagerBase {
public:
virtual void Login(string username, string password){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::DrawShape();
//........
}
};
class MobileMessagerLite : public MobileMessagerBase {
public:
virtual void Login(string username, string password){
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
MobileMessagerBase::DrawShape();
//........
}
};
class MobileMessagerPerfect : public MobileMessagerBase {
public:
virtual void Login(string username, string password){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::DrawShape();
//........
}
};
void Process(){
//编译时装配
Messager *m =
new MobileMessagerPerfect();
}
方法2
1.观察上述两个类,发现只有 *messager 声明不同,故采用基类声明,运行时多态调用方式,创建不同的 PCMessagerBase,Mobilemessager;
class PCMessagerLite {
Messager *messager; // = new PCMessagerBase()或 MobileMessagerBase()
public:
virtual void Login(string username, string password){
messager -> Connect();
//........
}
virtual void SendMessage(string message){
messager -> WriteText();
//........
}
virtual void SendPicture(Image image){
messager -> DrawShape();
//........
}
};
2.考虑步骤2的代码,Messager类是纯虚基类(抽象类),不能实例化,故= new ...不成立。
分析产生这种状况的原因,是Login,SendPicture等与平台实现相关的方法,和PlaySound,DrawShape等与业务功能相关的方法不应该在一个类里。
将其拆分,得到MessagerImp类。
同时将MessagerLite,MessagerPerfect类中相同的MesseagerImp字段提到父类Messager,得到重构后的代码
注意运行时装配
class Messager{
protected:
MessagerImp* messagerImp;//子类重复内容往上扔,放在父类里。
public:
virtual void Login(string username, string password)=0;
virtual void SendMessage(string message)=0;
virtual void SendPicture(Image image)=0;
virtual ~Messager(){}
};
class MessagerImp{
public:
virtual void PlaySound()=0;
virtual void DrawShape()=0;
virtual void WriteText()=0;
virtual void Connect()=0;
virtual MessagerImp(){}
};
//平台实现 n
class PCMessagerImp : public MessagerImp{
public:
virtual void PlaySound(){
//**********
}
virtual void DrawShape(){
//**********
}
virtual void WriteText(){
//**********
}
virtual void Connect(){
//**********
}
};
class MobileMessagerImp : public MessagerImp{
public:
virtual void PlaySound(){
//==========
}
virtual void DrawShape(){
//==========
}
virtual void WriteText(){
//==========
}
virtual void Connect(){
//==========
}
};
//业务抽象 m
//类的数目:1+n+m
class MessagerLite :public Messager {
public:
virtual void Login(string username, string password){
messagerImp->Connect();
//........
}
virtual void SendMessage(string message){
messagerImp->WriteText();
//........
}
virtual void SendPicture(Image image){
messagerImp->DrawShape();
//........
}
};
class MessagerPerfect :public Messager {
public:
virtual void Login(string username, string password){
messagerImp->PlaySound();
//********
messagerImp->Connect();
//........
}
virtual void SendMessage(string message){
messagerImp->PlaySound();
//********
messagerImp->WriteText();
//........
}
virtual void SendPicture(Image image){
messagerImp->PlaySound();
//********
messagerImp->DrawShape();
//........
}
};
void Process(){
//运行时装配
MessagerImp* mImp=new PCMessagerImp();
Messager *m =new Messager(mImp);
}
参考资料:1、李建忠 C++设计模式 2、 博客内容 http://www.cnblogs.com/wangxiaobao/