中国十佳企业网站设计公司,百度seo网站优化,个人免费网站怎么建设,网站建设 管理C设计模式#xff08;李建忠#xff09;
本文是学习笔记#xff0c;如有侵权#xff0c;请联系删除。
参考链接
Youtube: C设计模式
Gtihub源码与PPT#xff1a;https://github.com/ZachL1/Bilibili-plus
豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模…C设计模式李建忠
本文是学习笔记如有侵权请联系删除。
参考链接
Youtube: C设计模式
Gtihub源码与PPThttps://github.com/ZachL1/Bilibili-plus
豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模式李建忠2 面向对象设计原则里氏替换原则例子接口隔离原则例子 3 模板方法(Template Method)Motivation模板方法定义模板方法结构 4 策略模式StrategyMotivation策略模式的定义策略模式结构 5 观察者模式ObserverMotivation观察者模式定义观察者模式的结构 6 装饰模式DecoratorMotivation装饰模式定义装饰模式的结构 7 桥接模式Bridge桥接模式的定义桥接模式结构 后记 主要介绍SOLID原则Template Method, Strategy, Observer, Decorator, Bridge设计模式
2 面向对象设计原则
SOLID 是一组面向对象设计原则这些原则旨在帮助设计者创建更加灵活、可维护、可扩展且易于理解的软件系统。这五个原则分别是 单一职责原则Single Responsibility Principle - SRP 一个类应该只有一个引起变化的原因即一个类应该只有一个责任。这意味着一个类应该专注于一种类型的功能而不是承担多个不同的责任。 开放封闭原则Open/Closed Principle - OCP 软件实体类、模块、函数等应该对扩展开放对修改封闭。这意味着当需要添加新功能时应该通过扩展而不是修改现有代码来实现。 里氏替换原则Liskov Substitution Principle - LSP 子类应该能够替代父类并保持程序的正确性。也就是说对于基类的任何使用都应该能够在不知道是基类还是子类的情况下替代为子类。 接口隔离原则Interface Segregation Principle - ISP 不应该强迫客户端依赖于它们不使用的接口。一个类不应该强制实现它用不到的接口。应该根据实际需要定义更小、更具体的接口。 依赖倒置原则Dependency Inversion Principle - DIP 高层模块不应该依赖于低层模块而是应该依赖于抽象。抽象不应该依赖于具体实现具体实现应该依赖于抽象。这一原则促使使用接口或抽象类来实现松耦合。
这些原则共同构成了 SOLID 原则它们的目标是提供一种指导性的设计思想帮助开发者创建易于维护、可扩展且具有良好设计的软件系统。在实际应用中遵循这些原则有助于提高代码质量、降低维护成本并使系统更容易适应变化。
里氏替换原则例子
里氏替换原则Liskov Substitution Principle - LSP是 SOLID 原则中的一条它强调派生类子类应该能够替代其基类父类而不导致程序出错。以下是一个简单的 C 示例
#include iostream// 基类图形
class Shape {
public:virtual void draw() const {std::cout Drawing a shape. std::endl;}
};// 派生类1矩形
class Rectangle : public Shape {
public:void draw() const override {std::cout Drawing a rectangle. std::endl;}void calculateArea() const {std::cout Calculating the area of a rectangle. std::endl;}
};// 派生类2圆形
class Circle : public Shape {
public:void draw() const override {std::cout Drawing a circle. std::endl;}void calculateRadius() const {std::cout Calculating the radius of a circle. std::endl;}
};// 使用图形的函数
void drawShape(const Shape shape) {shape.draw();
}int main() {Rectangle rectangle;Circle circle;// 使用派生类替代基类drawShape(rectangle);drawShape(circle);return 0;
}在这个例子中Shape 是一个基类它有一个虚拟函数 draw表示绘制图形。然后有两个派生类Rectangle 和 Circle它们分别重写了 draw 方法。在 main 函数中我们使用 drawShape 函数来画不同的图形这里传入的是 Rectangle 和 Circle 对象它们是基类 Shape 的派生类。
这符合里氏替换原则因为我们可以使用 Rectangle 和 Circle 替代其基类 Shape而不会导致程序出错。虽然 Rectangle 和 Circle 有额外的方法但只要我们使用基类的方法就能够在不知道具体子类的情况下使用它们。
接口隔离原则例子
具体关于接口隔离原则ISP的例子
在 C 中通过接口类和多重继承可以很好地体现接口隔离原则ISP。下面是一个简单的 C 设计例子
#include iostream// 接口1动物接口
class IAnimal {
public:virtual void eat() 0;virtual void sleep() 0;
};// 接口2飞行动物接口
class IFlyable {
public:virtual void fly() 0;
};// 具体实现类狗
class Dog : public IAnimal {
public:void eat() override {std::cout Dog is eating. std::endl;}void sleep() override {std::cout Dog is sleeping. std::endl;}
};// 具体实现类鹰
class Eagle : public IAnimal, public IFlyable {
public:void eat() override {std::cout Eagle is eating. std::endl;}void sleep() override {std::cout Eagle is sleeping. std::endl;}void fly() override {std::cout Eagle is flying. std::endl;}
};int main() {Dog dog;Eagle eagle;// 使用动物接口IAnimal* animal1 dog;IAnimal* animal2 eagle;animal1-eat();animal1-sleep();animal2-eat();animal2-sleep();// 使用飞行动物接口IFlyable* flyer eagle;flyer-fly();return 0;
}在这个例子中IAnimal 是一个表示动物的接口包含了 eat 和 sleep 两个方法。IFlyable 是一个表示飞行动物的接口包含了 fly 方法。然后Dog 类实现了 IAnimal 接口而 Eagle 类实现了 IAnimal 和 IFlyable 两个接口。
这样设计的好处在于任何依赖于动物接口的代码都只需要关心 eat 和 sleep 方法而不必关心飞行的细节。同样依赖于飞行动物接口的代码也只需要关心 fly 方法。这符合接口隔离原则使得每个接口都小而专用不强迫客户端依赖于不需要的接口。
3 模板方法(Template Method)
Motivation
在软件构件过程中对于某项任务它常常有稳定的整体操作结构但各个子步骤却有很多改变的需求或者由于固有的原因比如框架和应用之间的关系而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下来灵活应对各个子步骤的变化或者晚期实现需求
适用性
一次性实现一个算法的不变的部分并将可变的行为留给子类来实现。
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
举例
库函数Library定义好流程但是其中某个具体操作留到调用者来实现
class Library {
public:// template methodvoid 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; // 变化的部分
};应用使用库函数Library
class Application : public Library { // 子类实现虚函数
protected:virtual bool Step2(){//... }virtual void Step4() {//... }
};int main()
{Library* lib new Application();lib-Run();delete lib;
}模板方法定义
定义一个操作中的算法的骨架而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法结构 上图中斜着的类是抽象类斜着的方法是抽象方法。
AbstractClass抽象类如Library
——定义抽象的原语操作primitive operation具体的子类将重定义它们以实现一个算法的各步骤。
——实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作也调用定义在AbstractClass或其他对象中的操作。
ConcreateClass具体类如Application
—— 实现原语操作以完成算法中与特定子类相关的步骤。
4 策略模式Strategy
Motivation
在软件构建过程中某些对象使用的算法可能多种多样经常改变如果将这些算法都写在对象中将会使对象变得异常复杂而且有时候支持不频繁使用的算法也是一个性能负担。
如何在运行时根据需要透明地更改对象的算法将对象和算法解耦
举例不同国家税率的计算新的需求是新增一个国家税率的计算。
不好的做法增加一个if else
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){ //...}//....}};好的做法定义一个新类扩展实现虚函数以此来实现不同的税率计算。
遵循开闭原则对扩展开放对修改关闭。 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(策略如TaxStrategy)
— 定义所有支持的算法的公共接口。Context使用这个接口来调用某 ConcreteStrategy定义的算法。
ConcreteStrategy(具体策略如CNTaxUSTax等)
— 以Strategy接口实现某具体算法。
Context(上下文如SalesOrder)
— 用一个ConcreteStrategy对象来配置。
— 维护一个对Strategy对象的引用。
— 可定义一个接口来让Strategy访问它的数据。
协作
•Strategy和Context相互作用以实现选定的算法。当算法被调用时 , Context可以将该算法所需要的所有数据都传递给该Strategy。或者Context可以将自身作为一个参数传递给Strategy操作。这就让Strategy在需要时可以回调Context。
• Context将它的客户的请求转发给它的Strategy。客户通常创建并传递一个ConcreteStrategy对象给该Context这样, 客户仅与Context交互。通常有一系列的ConcreteStrategy类可供客户从中选择。
含有许多条件语句的代码通常意味着需要使用Strategy模式
5 观察者模式Observer
Motivation
在软件构建过程中我们需要为某些对象建立一种“通知依赖关系”——一个对象的状态发生改变所有的依赖对象都将得到通知。如果这样的依赖关系过于紧密将使软件不能很好地抵御变化。
使用面向对象技术可以将这种依赖关系弱化并形成一种稳定的依赖关系从而实现软件体系结构的松耦合。
观察者模式定义
定义对象间的一种一对多的依赖关系以便当一个对象的状态发生改变时所有依赖于它的对象都得到通知并自动刷新。
这一模式中的关键对象是目标(subject)和观察者(observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变 , 所有的观察者都得到通知。作为对这个通知的响应每个观察者都将查询目标以使其状态与目标的状态同步。
这种交互也称为发布订阅puplish-subscribe。目标是通知的发布者。它发出通知时并不需知道谁是它的观察者。可以有任意数目的观察者订阅并接收通知。
代码场景分件切分器把大文件切分为小文件现在想要一个进度条显示分割进度
MainForm调用FileSplitter进行文件切分里面显示进度条
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();}
};FileSplitter的伪代码如下违反依赖倒置原则中抽象不应该依赖细节。
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);}}
};违反依赖倒置原则
依赖倒置原则Dependency Inversion PrincipleDIP是面向对象设计原则中的一条它是SOLID原则中的其中一项。依赖倒置原则强调的是高层模块不应该依赖于低层模块两者都应该依赖于抽象而且抽象不应该依赖于具体细节具体细节应该依赖于抽象。
利用观察者模式修正提供抽象基类为希望表示进度的类定义了一个接口。
FileSplitter和MainForm
// IProgress 是一个抽象基类为希望表示进度的类定义了一个接口。
class IProgress { // Observer
public:virtual void DoProgress(float value)0;virtual ~IProgress(){}
};class FileSplitter
{string m_filePath;int m_fileNumber;ListIProgress* 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.add(iprogress);}void removeIProgress(IProgress* iprogress){m_iprogressList.remove(iprogress);}protected:virtual void onProgress(float value) {ListIProgress*::iterator itor m_iprogressList.begin();while (itor ! m_iprogressList.end())(*itor)-DoProgress(value); // 更新进度多态可以是bar可以是console cout等等itor;}}
};
// **********不管添加多少个观察者以上的结构稳定不变// 继承Iprogress需要实现DoProgress从而自定义进度显示
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);// 两个进度展示一个是setValue为prograssBar另一个是console coutsplitter.addIProgress(this); // 观察者1splitter.addIProgress(cn); // 观察者2splitter.split();splitter.removeIProgress(this);}virtual void DoProgress(float value) {progressBar-setValue(value);}
};// 定义一个新类这是一个新的观察者新增进度显示的种类
class ConsoleNotifier : public IProgress {
public:virtual void DoProgress(float value){cout .;}
};
观察者模式的结构 参与者
• Subject目标
— 目标知道它的观察者。可以有任意多个观察者观察同一个目标。
— 提供注册和删除观察者对象的接口。
• Observer观察者上面代码中的IProgress
— 为那些在目标发生改变时需获得通知的对象定义一个更新接口。
• ConcreteSubject具体目标
— 将有关状态存入各ConcreteObserver对象。
— 当它的状态发生改变时, 向它的各个观察者发出通知。
• ConcreteObserver具体观察者
— 维护一个指向ConcreteSubject对象的引用。
— 存储有关状态这些状态应与目标的状态保持一致。
— 实现Observer的更新接口以使自身状态与目标的状态保持一致。
使用面向对象的抽象Observer模式使得我们可以独立地改变目标和观察者从而使两者之间的依赖关系达到松耦合。
目标发送通知时无需指定观察者通知会自动传播。
观察者自己决定是否需要订阅通知目标对象对此一无所知。
Observer模式是基于事件的UI框架中非常常用的设计模式也是MVC模式中的一个重要组成部分。
6 装饰模式Decorator
Motivation
在某些情况下我们可能会“过度地使用继承来扩展对象的功能”由于继承为类型引入的静态特质使得这种扩展方式缺乏灵活性并且随着子类的增多扩展功能的增多各种子类的组合扩展功能的组合会导致更多子类的膨胀。
如何使“对象功能的扩展”能够根据需要来动态地实现同时避免“扩展功能的增多”带来的子类膨胀问题想要使得任何“功能扩展变化”所导致的影响降到最低。
代码示例
考虑不同的流stream有文件流内存流网络流等需要对不同的流进行加密缓冲。 第一次设计每一个子类中都额外添加加密的操作有大量的代码重复bad smell
//业务操作
class Stream{
publicvirtual 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();}第二个版本把CryptoStream继承自Stream直接对Stream进行加密运行时绑定它的子类FileStreamNetworkStreamMemoryStream。这样可以省去冗余。
//业务操作
class Stream{publicvirtual 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;// 多种变化// new FileStream()// new NetworkStream()// new MemoryStream()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* s1new FileStream();CryptoStream* s2new CryptoStream(s1);BufferedStream* s3new BufferedStream(s1);BufferedStream* s4new BufferedStream(s2);
}第三个版本把CryptoStream和BufferedStream中的Stream* stream;提取出来放到DecoratorStream中。 //业务操作
class Stream{publicvirtual 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 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* s1new FileStream();CryptoStream* s2new CryptoStream(s1);BufferedStream* s3new BufferedStream(s1);BufferedStream* s4new BufferedStream(s2);}装饰模式定义
动态地给一个对象添加一些额外的职责。就增加功能来说 Decorator模式相比生成子类更为灵活。
装饰模式的结构 参与者
Component
——定义一个对象接口可以给这些对象动态地添加职责。
ConcreteComponent
——定义一个对象可以给这个对象添加一些职责。
Decorator
——维持一个指向Component对象的指针并定义一个与Component接口一致的接口。
ConcreteDecorator
——向组件添加职责。
通过采用组合而非继承的手法Decorator模式实现了在运行时动态扩展对象功能的能力而且可以根据需要扩展多个功能。避免了使用继承带来的灵活性差和多子类衍生问题。
Decorator类在接口上表现为is-a Component的继承关系即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系。
Decorator模式的要点在于解决主体类在多个方向上扩展功能的问题。
7 桥接模式Bridge
Motivation
由于某些类型的固有实现逻辑使得它们具有两个变化的维度乃至多个变化维度。
如何应对这种多维度的变化如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化而不引入额外的复杂度
代码示例有通信模块Messager现在要支持PC平台和mobile平台的设计分别需要有lite版本和perfect版本。
不好的代码
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(){}
};//平台实现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(){//}
};//业务抽象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();
}
好的代码将业务功能Messager和平台实现MessagerImp分离
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(){//}
};//业务抽象 mclass 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* mImpnew PCMessagerImp();Messager *m new Messager(mImp);
}
桥接模式的定义
将抽象部分与它的实现部分分离使它们都可以独立地变化。
桥接模式结构 Abstraction (Messager) — 定义抽象类的接口。 — 维护一个指向Implementor类型对象的指针。
RefinedAbstraction (MessagerLiteMessagerPerfect) — 扩充由Abstraction定义的接口。
Implementor (MessagerImp) — 定义实现类的接口该接口不一定要与Abstraction的接口完全一致事实上这两个接口可以完全不同。一般来讲Implementor接口仅提供基本操作而Abstraction则定义了基于这些基本操作的较高层次的操作。
ConcreteImplementor (PCMessagerImp, MobileMessagerImp) — 实现Implementor接口并定义它的具体实现。
Bridge模式有以下一些优点
分离接口及其实现部分一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置一个对象甚至可以在运行时刻改变它的实现。将Abstraction与Implementor分离有助于降低对实现部分编译时刻的依赖性当改变一个实现类时并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性一定要有这个性质。另外接口与实现分离有助于分层从而产生更好的结构化系统系统的高层部分仅需知道Abstraction和Implementor即可。提高可扩充性你可以独立地对Abstraction和Implementor层次结构进行扩充。实现细节对客户透明你可以对客户隐藏实现细节例如共享Implementor对象以及相应的引用计数机制如果有的话。
后记
截至2024年1月16日花费1天时间学习前5个设计模式后面继续学习。