C++ 面向对象设计
- 封装:隐藏内部实现
- 继承:复用现有代码
- 多态:改写对象行为
设计模式关键在于分解和抽象;
设计模式的主要目的是易于变化
面向对象设计原则–比设计模式更加重要 违背了设计原则,设计模式是错误的。
- 依赖倒置原则(DIP)
- 开放封闭原则(OCP)
- 单一职责原则(SRP)
工厂方法模式
模式定义
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
模式结构和时序图
- Product:抽象产品
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
模式结构:
工厂方法模式的优点
- 工厂无需关心细节,甚至无序知道具体产品的类名称。
- 工厂角色和茶农角色的多态性设计是工厂方法模式的关键。它能确定创建的产品对象;创建细节完全封装在具体工厂内部。所有的具体工厂类都具有同一抽象父类。
- 添加新产品时,只需要添加一个具体的工厂和具体产品就可以了,系统的扩展性较好,完全符合”开闭原则”。
工厂模式的缺点
- 添加新产品时,需要编写新的具体产品类,而且需要提供与之赌赢的工厂类,系统中类的个数将成对增加,会带来额外的编译开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
单例模式
模式定义
单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。
模式分析
单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
在单例模式的实现过程中,需要注意如下三点:
- 单例类的构造函数为私有;
- 提供一个自身的静态私有成员变量;
- 提供一个公有的静态工厂方法。
优点
缺点
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
适用环境
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
观察者模式
上述模式中,需要使用明显的调用函数,来进行两个对象之间的通信,但是能否建立一种关系,使得一个对象发生改变时,自动通知其它对象,做出反映。这种模式就是观察者模式
模式定义
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式。
观察者模式是一种对象行为型模式。
模式结构
观察者模式包含如下角色:
- Subject: 目标主题:跟踪所有观察者,并提供添加和删除观察者的接口。
- ConcreteSubject: 具体目标;将有关状态存入各 ConcreteObserver 对象。当具体主题的状态发生任何更改时,通知所有观察者。
- Observer: 观察者:为所有的具体观察者定义一个接口,在得到主题的通知时进行自我更新。
- ConcreteObserver: 具体观察者;实现 Observer 所要求的更新接口,以便使本身的状态与主题的状态相协调。
类图如下:
优缺点
优点:
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
适用情况
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
代理模式
模式定义
代理模式(Proxy Pattern) :给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy或Surrogate,它是一种对象结构型模式。
模式结构
代理模式包含角色如下:
- Subject:抽象主题角色;声明了 RealSubject 与 Proxy 的共同接口,定义了某个/些功能。
- RealSubject(真实主题):通常执行具体的业务逻辑,Proxy 控制对它的访问。
- Proxy:持有一个 RealSubject 引用(指针),可以在需要时将请求转发给 RealSubject,以此起到代理的作用。
- Client(客户端):通过 Proxy 间接地与 RealSubject 进行交互。
注意: Proxy 和 RealSubject 都实现了 Subject 的接口,这允许 Client 可以像处理 RealSubject 一样处理 Proxy。
优缺点
优点:
- 代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度。
- 在客户端和目标对象之间,代理起到一个中介作用,这样可以保护目标对象。在对目标对象调用之前,代理对象也可以进行其他操作。
缺点:
- 这种模式引入了另一个抽象层,这有时可能是一个问题。如果真实主题被某些客户端直接访问,并且其中一些客户端可能访问代理类,这可能会导致不同的行为。
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
适用场景
- 远程代理(Remote Proxy):为一个位于不同地址空间的对象提供一个本地代理,对代理的方法调用会导致对远程对象的方法调用。ATM 就是一个例子,ATM 可能会持有(存在于远程服务器中的)银行信息的一个代理对象。
- 虚拟代理(Virtual Proxy):使用虚拟代理,代理可以作为一个(资源消耗较大的)对象的代表。虚拟代理经常延迟对象的创建,直到需要为止。在创建对象之前(及创建对象过程中),虚拟代理也可以作为对象的代理;之后,代理将请求直接委托给 RealSubject。
- 保护代理(Protection Proxy):根据访问权限,可以使用保护代理来控制对资源的访问。
装饰模式
对于类或者对象的行为增加,一般有两种方式:
- 继承机制:使用继承机制,直接进行函数的添加。
- 关联机制,即将一个类的对象嵌入另外一个对象中,由另外一个对象来决定是否调用嵌入对象的行为,以便扩展自己的行为。,我们称这个嵌入的对象为“装饰器”。
模式定义:
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。
模式结构
包含角色如下:
- Component: 抽象构件
- ConcreteComponent: 具体构件
- Decorator: 抽象装饰类
- ConcreteDecorator: 具体装饰类
装饰模式中的装饰类一般拥有为抽象构建成员,通过具体构建类来进行添加操作,成为具体的装饰类,最终实现具体的类操作的扩展,装饰器一般也来自于基础的抽象构件
优点
缺点
- 使用装饰模式时,因为层层递进关系,会产生很多无用的小对象;和具体的装饰类。
适配器模式
适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
适配器模式(Adapter Pattern)是一种补救模式,将一个类的接口转换成客户希望的另外一个接口,从而使原本由于接口不兼容而不能一起工作的类可以一起工作。
1.3 模式结构
适配器模式包含如下角色:
- Target:目标抽象类
- Adapter:适配器类
- Adaptee:适配者类
- Client:客户类
适配器模式有对象适配器和类适配器两种实现:
对象适配器:
优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性。
对象适配器模式还具有如下优点: 一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
缺点
- 对象适配器:
-mWsqMa1A-1704852256417)]
优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性。
对象适配器模式还具有如下优点: 一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
缺点
- 对象适配器:
- 与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。