策略模式(Strategy)
定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。
策略模式的优点:
- 将算法单独封装,减少算法和算法调用者直接的耦合。
- 合理使用继承可以提取算法中的公共部分。
- 简化单元测试。
策略模式的缺点:
- 策略模式只适用于客户端知道所有的算法或行为的情况。
- 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。
策略模式的适用场景:
- 需要在不同情况下使用不同的算法,以后算法可能还会增加。
- 对用户隐藏算法逻辑。
简单工厂模式(Static Factory Method)
由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
简单工厂模式的优点:
- 通过使用工厂类,外界仅仅需要负责“消费”对象,而不必管这些对象究竟如何创建及如何组织的。明确了各自的职责和权利,有利于整个软件体系结构的优化。
简单工厂模式的缺点:
- 由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中,它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。违反了开闭原则。
- 当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求。这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利。
简单工厂模式的适用场景:
- 工厂类负责创建的对象比较少。
- 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。
装饰模式(Decorator)
动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰模式的优点:
- 装饰模式动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
- 每个装饰对象的实现和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链中。
- 可以把类的核心职责和装饰功能区别开来,从而去除相关重复的装饰逻辑。
装饰模式的缺点:
- 装饰模式会导致设计中出现许多小类。
装饰模式的适用场景:
- 需要扩展一个类的功能,或给一个类添加附加职责。
- 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实
代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
代理模式的优点:
- 职责清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
- 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
- 高扩展性。
代理模式的缺点:
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
代理模式的适用场景:
- 远程代理,为一个对象在不同的地址空间提供局部代表,这样就可以隐藏一个对象存在于不同地址空间的事实。
- 虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
- 安全代理,用来控制真实对象访问时的权限。
- 智能指引,是指当调用真实的对象时,代理处理另外的一些事情。
工厂方法模式(Factory Method)
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法是一个类的实例化延迟到其子类。
工厂方法模式的优点:
- 工厂类中包含了必要的逻辑判断,根据客户端的选择条件的拿给她实例化相关的类,对于客户端来说去除了与具体产品的依赖。
- 工厂方法模式的扩展性非常优秀。
- 工厂方法模式是典型的解耦框架。
工厂方法模式的缺点:
- 使用者必须知道相应工厂的存在。
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
工厂方法模式的适用场景:
- 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
抽象工厂模式(Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式的优点:
- 具体产品在应用层代码隔离,无须关心创建细节
- 将一系列的产品族统一到一起创建
抽象工厂模式的缺点:
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
- 增加了系统的抽象性和理解难度
抽象工厂模式的适用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现
原型模式(Prototype)
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式的优点:
- 隐藏了对象创建的细节。
- 提高了性能。
- 不用重新初始化,动态获得对象运行时的状态。
原型模式的缺点:
- 适用性不是很广。
- 每一个类必须配备一个克隆方法。
原型模式的适用场景:
- 某些结构复杂的对象的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。
- 一般在初始化的信息不发生变化的情况下,克隆是最好的方法。
观察者模式(Observer)
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有的观察者对象,使他们能够自动更新自己。
观察者模式的优点:
- 观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
- 观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知。
观察者模式的缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
- 如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
- 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
观察者模式的适用场景:
- 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
- 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
单例模式(Singleto)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式的优点:
- 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就
防止其它对象对自己的实例化,确保所有的对象都访问一个实例 - 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
- 提供了对唯一实例的受控访问。
- 由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。
- 避免对共享资源的多重占用。
单例模式的缺点:
- 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
- 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
单例模式的适用场景:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
命令模式(Command)
将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式的优点:
- 命令和实际的执行者分离,实际的操作都是通过invoker以及receiver去执行的
- 屏蔽了底层的复杂实现,对外提供了统一的表现
- 可以记录操作的历史记录
- 拓展性好,一方面,可以很方便地添加新的命令,如在word中添加插入html功能;另一方面,也可以把命令迁移到另一种场景下,如把word中的命令迁移到excel中
- 用户使用上,用户通过组合一些命令,可以实现宏的功能,效率更高
命令模式的缺点:
- 代码冗余较多。每个命令,需要同时修改好几个地方,维护较复杂
命令模式的适用场景:
- 有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系
责任链模式(Chain of Responsibility)
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理为止。
责任链模式的优点:
- 责任链模式将请求和处理分开,请求者不知道是谁处理的,处理者可以不用知道请求的全貌。
- 提高系统的灵活性。
责任链模式的缺点:
- 降低程序的性能。每个请求都是从链头遍历到链尾,当链比较长的时候,性能会大幅下降。
- 不易于调试。由于该模式采用了类似递归的方式,调试的时候逻辑比较复杂。
责任链模式的适用场景:
- 一个请求需要一系列的处理工作。
- 业务流的处理,例如文件审批。
- 对系统进行扩展补充。
适配器模式(Adapter)
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式的优点:
- 将目标类和适配者类解耦
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性
- 灵活性和扩展性都非常好,符合开闭原则
适配器模式的缺点:
- 过多的适配,会让系统非常的凌乱,不容易整理进行把握。因此如果不是很有必要,可以不使用适配器,而似乎直接对系统进行重构。
适配器模式的适用场景:
- 想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
- 我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
备忘录模式(Memento)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
备忘录模式的优点:
- 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时,使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
- 本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
- 当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
备忘录模式的缺点:
- 如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
- 当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很昂贵。
- 当发起人角色的状态改变的时候,有可能这个协议无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。
备忘录模式的适用场景:
- 需要保存/恢复数据的相关状态场景。
- 提供一个可回滚的操作。
桥接模式(Bridge )
将抽象部分与它的实现部分分离,使他们都可以独立的变化。
桥接模式的优点:
- 抽象和实现分离。桥梁模式完全是为了解决继承的缺点而提出的设计模式
- 优秀的扩展能力
- 实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装
桥接模式的缺点:
- 会增加系统的理解与设计难度。由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
桥接模式的适用场景:
- 不希望或不适用使用继承的场景
- 接口或抽象类不稳定的场景
- 重用性要求较高的场景