简单工厂模式
- 定义:定义一个工厂类,它可以根据参数的不同赶回不同类的实例,被创建的实例通常具有共同的父类。
- 在简单工厂模式中用于创建实例的方法通常是静态方法,因此又被称为静态工厂方法模式
- 需要什么,只传入相应的正确的参数,就可以获取所需要的对象,而无须知道其创建细节
Java语言创建对象的几种方式
- new关键字直接创建对象
- 反射机制
- 克隆方法
- 工厂类创建对象
优点:
- 实现了对象创建和使用分离
- 客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性
缺点:
- 工厂类的职责过重,一旦不能正常工作,整个系统都要受到影响
- 增加系统中类的个数,增加了系统的复杂度和理解难度(噫–感觉有好多模式都增加了类的个数-_-|)
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑
- 静态工厂方法,无法形成基于继承的等级结构,工厂类不能得到很好地扩展
模式适用环境
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂
- 客户端只需要知道传入工厂类的参数,对于如何创建对象并不关心
工厂方法模式
- 由于简单工厂模式存在的一些缺陷(违背了开闭原则),工厂方法模式利用继承结构对简单工厂模式做出了优化
定义:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。(类创建型模式)
Factory Method Pattern:Defined an interface for creating an object,but let subclassess decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.
工厂方法模式包括以下4个角色:
- 抽象产品(产品的父类)
- 具体产品
- 抽象工厂(工厂的父类)
- 具体工厂
工厂方法的隐藏
- 目的:为了进一步简化客户端的使用
- 实现:在工厂类中直接调用产品类的业务方法,客户端无须调用工厂方法创建产品对象,直接使用工厂对象即可调用所创建的产品对象中的业务方法
- 此时工厂方法模式父类为抽象类,业务方法在父类中已经实现(创建产品,调用业务方法)
factory.writeLog()
优点:
- 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,客户只需要知道在配置文件中调用哪一个具体的工厂类
- 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
- 在系统中加入新产品时,完全符合开闭原则
缺点:
- 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
- 增加了系统的抽象性和理解难度
模式使用环境
- 客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)
- 抽象工厂类通过其子类来指定创建哪个对象
抽象工厂模式
- 工厂方法模式存在的一些问题:每个具体工厂只有一个或者一组重载的工厂方法,只能生产一种产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销
抽象工厂模式:
- 一个工厂可以生产一系列产品(一族产品),极大减少了工厂类的数量
一些概念
- 产品等级结构:产品等级结构即产品的继承结构(比如:电视–海尔电视,海信电视,TCL王牌)
- 产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品(所有不同类型的产品)
模式动机
- 当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型额具体产品时就可以使用抽象工厂模式
- 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式
定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。(对象创建型模式)
Abstract Factory Pattern:Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
当一个工厂的等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率
抽象工厂模式包含以下4个角色:
- 抽象工厂
- 具体工厂
- 抽象产品
- 具体产品
开闭原则的倾斜性
- 对于增加新的产品族,抽象工厂模式很好地支持了开闭原则,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无需做任何修改
- 对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了开闭原则
优点:
- 隔离了具体类的生成,使得客户端并不需要知道什么被创建
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
- 增加新的产品族很方便,无需修改已有系统,符合开闭原则
缺点
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不变,违背了开闭原则
模式适用环境
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节
- 系统中有多于一个的产品族,但每次只使用其中某一产品族
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
- 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构
单例模式
定义:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。(对象创建型模式)
Singleton Pattern:Ensure a class has only one instance,and provide a global point of access to it.
要点:
- 某个类只能有一个实例
- 必须自行创建这个实例
- 必须自行向整个系统提供这个实例
单例模式的实现
- 私有构造函数
- 静态私有成员变量(自身类型)
- 静态公有的工厂方法
饿汉式单例
private static final EagerSingleton instance = new EagerSingleton()
懒汉式单例
- 对方法体加上线程锁:synchronized
- 对创建代码块加上线程锁:synchronized(LazySingleton.class){//创建代码块}
- 双重检查锁定(Double-Check Locking):先判断对象是否为空,再锁代码块,代码块中再检查对象是否为空
- Java语言中最好的实现方式:IoDH,使用静态内部类
饿汉式单例与懒汉式单例比较
- 饿汉式:无需考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长
- 懒汉式单例:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响
优点:
- 提供了对唯一实例的受控访问
- 可以节约系统资源,提高系统的性能
- 允许可变数目的实例(多例类)
缺点:
- 扩展困难
- 单例类的职责过重
- 自动垃圾回收机制,可能会导致共享的单例对象的状态丢失
适用环境:
- 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象
- 客户调用类的单个实例只允许适用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例
适配器模式
定义:将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。
Adapter Pattern:Convert the interface of a class into another interface clients expect.Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
定义中所提及的接口是指广义的接口,它可以表示一个方法或者方法的集合
类适配器
- 实现客户端要调用的目标接口,继承待适配的类,然后调用业务方法。整个过程中没有产生对象,故被称为类适配器。
对象适配器
- 继承客户端要调用的目标类,在适配器中创建待适配的类的对象,由此对象来调用具体的业务方法。(关联关系)
适配器模式包含以下3个角色:
- 目标抽象类
- 适配器类
- 适配者类
缺省适配器模式
- 定义:当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中的每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
优点:
- 将目标类和适配器解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
- 增加了类的透明性和复用性,提供了适配者的复用性,同一个适配者类可以在多个不同的系统中复用
- 灵活性和扩展性非常好
- 类适配器模式:置换一些适配者的方法很方便
- 对象适配器模式:可以吧多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类
缺点:
- 类适配器:(1)一次最多只能适配一个适配者类,不能同时适配多个适配者;(2)适配者类不能为最终类;(3)目标抽象类只能为接口,不能为类
- 对象适配器:在适配器中置换适配者类的某些方法比较麻烦
模式使用环境
- 系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码
- 创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作
桥接模式
定义:将抽象部分与它的实现部分解耦,使得两者都能够独立变化
Bridge Pattern: Decouple an abstraction from its implementation so that the two can vary independently.
桥接模式包含以下四个角色:
- 抽象类
- 扩充抽象类
- 实现类接口
- 具体实现类
桥接模式与适配器模式的联用
- 桥接模式:用于系统的初步设计,对于存在两个独立变化纬度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化
- 适配器模式:当发现系统与已有类无法协同工作时
优点:
- 分离抽象接口及其实现部分
- 取代了多层继承方案,极大地减少了子类的个数
- 提高了系统的可扩展性,在两个变化纬度任意扩展一个维度,不需要修改原有系统,符合开闭原则
缺点:
- 会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程
- 正确识别出系统中两个独立变化的维度并不是一件容易的事情
模式适用环境:
- 需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系
- 抽象部分和实现部分可以以继承的方式独立扩展互不影响
- 一个类存在两个或多个独立变化的维度,且这两个或多个维度都需要独立地进行扩展
- 不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统
组合模式
定义:组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合对象。
Composite Pattern:Compose object into tree structures to represent part-whole hierarchies.Composite lets clients thret individual objects and compositions of objects uniformly.
透明组合模式
- 抽象构建Component中声明了所有用于管理成员对象的方法,包括add,remove,getChild等方法
- 在客户端看来,叶子对象与容器对象所提供的方法是一致的,客户端可以一致地对待所有的对象
- 缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的
安全组合模式
- 抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明
- 对于叶子对象,客户端不可能调用到这些方法
- 缺点是不够透明,客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件
优点
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码
- 增加新的容器构件和叶子构件都很方便,符合开闭原则
- 为树形结构的面向对象实现提供了一种灵活的解决方案。
缺点
- 在增加新构件时很难对容器中的构件类型进行限制
模式适用环境
- 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型
外观模式
定义:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Facade Pattern:Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use.
是迪米特法则的一种具体实现
- 通过引入一个新的外观角色来降低原有系统的复杂度,同时降低客户类与子系统的耦合度
- 子系统是一个广义的概念,它可以是一个类、一个功能模块、系统的一个组成部分或者一个完整的系统
优点:
- 对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易
- 实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可
- 一个子系统的修改对其他子系统没有任何影响,而且子系统的内部变化也不会影响到外观对象
缺点:
- 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性
- 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。
模式适用环境
- 要为访问一系列复杂的子系统提供一个简单入口
- 客户端程序与多个子系统之间存在很大依赖性
- 在层次结构中,可以使用外观模式的定义系统中每一层的入口,层与层之间不直接产生联系,降低层之间的耦合度
代理模式
- 子类型:远程代理、虚拟代理、保护代理、缓冲代理、智能引用代理…
定义:给某一个对象提供一个代理或占位符,并由代理对象来控制对源对象的访问
Proxy Pattern:Provide a surrogate or placeholder for another object to control access to it
代理对象在客户端对象和目标对象之间起到中介的作用
- 去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务
代理模式包含以下3个角色:
- Subject
- Proxy
- RealSubject
优点:(智能引用代理)
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无需修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)
模式适用环境:
- 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理
职责链模式
定义:避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止
Chain of Responsibility Pattern:Avoid coupling the sender of request to its receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.
将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行相应的处理
- 客户端无须关心请求的处理细节以及请求的传递,只需将请求发送到链上,将请求的发送者和请求的处理者解耦
职责链模式包含以下两个角色
- Handler
- ConcreteHandler
纯的职责链模式
- 一个具体处理者对象只能在两个行为中选择一个:要么承担全部责任,要么将责任推给下家
- 不允许出现某一个具体处理者对象在承担了一部分或者全部责任后又将责任向下传递的情况
- 一个请求必须被某一个处理者对象所接收,不能出现某个请求违背任何一个处理者对象处理的情况
不纯的职责链模式
- 允许某个请求被一个具体处理者部分处理后向下传递,或者一个具体处理者处理完请求后其后继处理者可以继续处理该请求
- 一个请求可以最终不被任何处理者对象所接收并处理
- ex:Javascript中的事件浮升处理机制(Bubbling)
优点:
- 使得一个对象无须知道是其他哪一个对象处理其请求,降低了系统的耦合度
- 可简化对象之间的相互连接
- 给对象职责的分配带来更多的灵活性
- 增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可
缺点:
- 不能保证请求一定会被处理
- 对于比较长的职责链,系统性能将受到一定影响,在进行代码调试时不太方便
- 如果建链不当,可能会造成循环调用,将导致系统陷入死循环
模式适用环境
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
- 可动态指定一组对象处理请求
命令模式
定义:将一个请求封装为一个对象,从而让你可以用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作
Command Pattern:Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.
命令模式包含以下4个角色:
- Command
- ConcreteCommand
- Invoker
- Receiver
命令模式的本质是对请求进行封装
- 一个请求对应于一个命令,将发出命令的责任和执行命令的责任分开
- 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的
- 宏命令:组合命令,它是组合模式和命令模式联用的产物
优点:
- 降低了系统的耦合度
- 新的命令可以很容易地加入到系统中,符合开闭原则
- 可以比较容易地设计一个命令队列或者宏命令
- 为请求的撤销和恢复操作提供了一种设计和实现方案
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类(针对每一个对请求接收者的调用操作都需要设计一个具体命令类)
模式适用环境:
- 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
- 系统需要在不同的时间制定请求、将请求排队和执行请求
- 系统需要支持命令的撤销操作和恢复操作
- 系统需要将一组操作组合在一起形成宏命令
观察者模式
定义:定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新
Observer Pattern:Defien a on-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.
观察者模式包含以下4个角色:
- Subject
- ConcreteSubject
- Observer
- ConcreteObserver
如果在具体层之间具有关联关系,系统的扩展性将受到一定的影响,增加新的具体目标类有时候需要修改原有观察者的代码,在一定程度上违背了开闭原则,但是如果原有观察者的类无须关联新增的具体目标,则系统扩展性不受影响
优点:
- 可以实现表示层和数据逻辑层的分离
- 在观察目标和观察者之间建立一个抽象的耦合
- 支持广播通信,简化了一对多系统设计的难度
- 符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便
缺点:
- 将所有的观察者都通知到会花费很多时间
- 如果存在循环依赖可能会导致系统崩溃
- 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化
模式适用环境:
- 一个方面依赖另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用
- 一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁
- 需要在系统中创建一个触发链
策略模式
定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它的客户变化。
Strategy Pattern:Define a family of algorithms,encapsulate each one,and make them interchangeable.Strategy lets the algorithm vary independently from clients that use it.
策略模式包含以下3个角色:
- Context
- Strategy
- ConcreteStrategy
优点:
- 提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
- 提供了管理相关的算法族的办法
- 提供了一种可以替换继承关系的办法
- 可以避免多重条件选择语句
- 提供了一种算法的复用机制,不同的环境类可以方便地复用策略类
- 缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 将造成系统产生很多具体策略类
- 无法同时在客户端使用多个策略类
- 适用环境:
- 一个系统需要动态地在几种算法中选择一种
- 避免使用难以维护的多重条件选择语句
- 不希望客户端知道复杂的、与算法相关的数据结构,提高算法的保密性与安全性
模板方法模式
模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类。模板方法模式使得子类不改变一个算法的结构即可重定义该算法的某些特定步骤。
Template Method Pattern: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
是一种基于继承的代码复用技术
- 将一些复杂流程的实现步骤封装在一系列基本方法中
- 在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来怪某些步骤,从而使得相同的算法框架可以有不同的执行结果
模板方法模式包含以下2个角色:
- AbstractClass
- ConcreteClass
优点:
- 在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中步骤的执行次序
- 提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为
- 可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行
- 更换和增加新的子类很方便,符合单一职责原则和开闭原则
缺点:
- 需要为每一个基本方法的不同实现提供一个子类,如果父类中可变的基本方法太多,将会导致类的个数增加,系统会更加庞大,设计也会更加抽象(可结合桥接模式)
适用环境:
- 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现
- 各子类中公共的行为应被提取出来,并集中到一个公共父类中,以避免代码重复
- 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制