基础设计模式

定义

设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。设计模式是实现特定需求的接近代码层面的设计套路,通常用于梳理和表达对象之间较为复杂的依赖和交互关系,将错综复杂的容易膨胀的难以理解和扩展的条件判断逻辑解开成一系列对象的清晰可理解的易扩展的交互结构。

使用目的

为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编写真正工程化;设计模式使软件工程的基石脉络。

设计模式是软件设计的入门指南,其主要理念是:面向接口的编程,组合与委托优先于基础。理解设计模式的关键点:关注点及目标用途、协作对象、静态结构及动态交互。

常用基本设计模式

工厂模式(FactoryMethod)

  • 根据不同条件为客户端创建实现相同接口和功能的实例,而客户端使用的是统一的功能接口。
  • 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
  • 使用场景:
    • 当一个类不知道它所必须创建的对象的类的时候
    • 当一个类希望由它的子类来指定它所创建的对象的时候
    • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

单例模式(Singleton)

  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点。该类需要跟踪单一的实例,并确保没有其他实例比创建。单例类适合于需要通过单个对象访问全局资源的场合。

生成器模式(Builder)

  • 复杂对象的创建,需要多个步骤或组合多个子部件完成,并且在这些步骤或组件完成之前,不允许该实例的生成。通常会私有化构造器,在构造的每一步骤中会设置某些属性并返回其自身,便于链式调用,提供一个build方法生成满足某种实例创建契约的最终实例。
  • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  • 使用场景:
    • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
    • 当构造过程必须允许被构建的对象有不同的表示时。

抽象工厂模式(AbstractFactory)

  • 系统含有多个相互协作的不同功能和特性的组件,每个组件可定制为不同的种类和风格。抽象工厂为每个组件的创建提供相对应的抽象工厂的创建接口,而由具体的工厂实现对应的组件创建接口。
  • 提供一个接口,用于创建与某些对象相关或依赖于某些对象的类家族,而又不需要指定它们的具体类。通过这种模式可以去除客户代码和来自工厂的具体对象细节之间的耦合关系。
  • 类簇是一种把一个公共的抽象超类下的一些私有的具体子类组合在一起的架构。抽象超类负责声明创建私有子类实例的方法,会根据被调用方法的不同分配恰当的具体子类,每个返回的对象都可能属于不同的私有子类。

原型模式(Prototype)

  • 从现有对象实例快速生成和定制化对象实例,而不需要创建大量子类。原型模式特别适合于定制化。
  • 比如,现有一个房间对象,要创建含有炸弹的房间对象;这些房间对象的房间属性都是一样的,而这些炸弹可能是不同种类,也可能是相同种类只是颜色不同,使用普通的继承结构和初始化操作会产生大量的相同的赋值代码。此时使用原型模式非常恰当。通过一个含有普通炸弹的房间对象作为原型进行克隆,然后设置炸弹的种类和属性即可。
  • 原型模式使用有两点:需要一个原型管理器进行原型注册和查找;根据场景正确地实现浅拷贝和深拷贝。
  • 为原型静态或动态地增加属性和行为,可以轻易地为所有从该原型衍生的对象添加属性和行为。

适配器模式(Adapter)

  • 将一个类的接口转换成另外一个客户希望的接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 复用现有类或对象的功能,实现客户端需要的接口。有类适配器和对象适配器两种。类适配器继承现有类,可以重新定义部分行为;对象适配器使用现有类的对象实例并委托实现给该对象,可灵活使用多个现有对象。通常使用对象适配器。

组合模式(Composite)

  • 具有层次性结构的单个对象与组合对象具有一致的接,客户端能够使用同一的接口操作局部和整体。组合模式最大的优点是他的节点可以自由增加,且调用节点方便。

外观模式(Facade)

  • 为复杂子系统提供简单易用的接口,屏蔽实现的复杂性。通过减少复杂度和隐藏子系统之间的通讯和依赖性,使子系统更加易于使用。

装饰器模式(Decorator)

  • 具有相同接口的对象可以在运行时动态组合成具有复合行为的对象。可以在运行时为现有对象动态添加任意复杂的额外行为。
  • 这种模式动态地将额外的责任附加到一个对象上。在进行功能扩展时,装饰是子类化之外的一种灵活的备选方法。和子类化一样,采纳装饰模式可以加入新的行为,而又不必修改已有的代码。装饰将需要扩展的类的对象进行包装,实现与该对象相同的接口,并在将任务传递给被包装对象之前或之后加入自己的行为。装饰模式表达了这样的设计原则:类应该接纳扩展,但避免修改。
  • 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

代理模式(Proxy)

  • 为对象或服务提供间接访问机制。提供间接访问的原因可能有:隐藏源对象或源服务的部署细节(远程代理);源对象创建开销很大,只有需要的时候才创建(虚代理);访问源对象需要某种权限或同步互斥(保护代理);需要为访问源对象和源服务增加额外的行为(智能代理)。代理对象与源对象通常有一致的接口,并持有源对象的引用。
  • 涉及的角色:
    • 协议:定义代理和委托的共同接口(方法)
    • 委托:根据指定的协议,委托代理去完成实现指定接口(方法)
    • 代理:根据指定的协议,实现委托需要实现的接口(方法)

桥接模式(Bridge)

  • 抽象部分与实现部分各具有独立的继承层次结构分开变化发展,使用桥接器将抽象继承层次与实现继承层次连接起来,实现并不直接继承抽象类,而是抽象类持有实现类的引用。
  • 通常适用于这样的场景:创建一个标准协议的接口框架,而接口框架的实现可以有多种选择,客户端可以根据需要选择不同的实现。
  • 桥接器通常与抽象工厂模式、单例模式组合使用。

享元模式(Flyweight)

  • 运用共享技术有效地支持大量细粒度的对象。
  • 可使用缓存技术实现。为了尽可能节省内存开销,需要识别享元对象中的外部状态并从中移除,使得享元对象的共享达到最大化。
  • 适用场景:
    • 一个应用程序适用了大量的对象。
    • 完全由于使用大量的对象,造成很大的存储开销。
    • 对象的大多数状态都可变为外部状态。
    • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
    • 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

模板方法模式(TemplateMethod)

  • 预先定义处理流程,对于流程中某些环节的具体处理留给具体业务去完成。模板方法模式应指明哪些是钩子操作(可选重定义,有默认实现),哪些是抽象操作(必须重定义,没有默认实现)。
  • 这种模式为某个操作中的算法定义框架,并将算法中的某些步骤推迟到子类实现。模板方法模式使子类可以重定义一个算法中的特定步骤,而不需要改变算法的结构。

策略模式(Strategy)

  • 为特定任务提供多种实现算法,并在不同场景下可灵活切换而对客户端透明。比如批量拉取数据的顺序策略以及并发策略。使用策略模式时,客户端可持有策略实现对象的接口或者策略实现对象的模板接口参数。

观察者模式(Observer)

  • 每个对象都有一系列观察者,当对象发生更新时推送更新给观察者,由观察者决定具体行为。通常引申为订阅-消费模式。
  • 这种模式定义一种对象间一对多的依赖关系,使得当一个对象的状态发生变化时,其他具有依赖关系的对象可以自动地被通知和更新。主体和观察者具有宽松的耦合关系。观察和被观察对象之间可以进行通讯,而不需要太多地了解对方。

职责链模式(ResponsibilityChain)

  • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
  • 将能够处理某种请求的多个对象串联成一条处理链,请求沿着处理链传递给这些对象,对请求的发送者隐藏处理请求的实现细节(请求的发送者不需要知道谁真正处理这个请求)。责任链上的请求处理对象可以在运行时动态增减。责任链的实现中,链的构造和请求的传递既可以使用全局控制器来配置、生成和控制(集中式策略),也可以通过链上的对象来负责引用和传递(分散式策略)。需要构造两种接口:请求接口和请求处理对象的接口,而请求处理对象则需要实现请求接口。请求处理对象的顺序也值得斟酌,通常是从特殊到普遍的顺序。异常捕获和处理机制即是职责链模式的具体应用例子之一。

命令模式(Command)

  • 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。请求对象将一或多个动作绑定在特定的接收者上。命令模式将发出请求的对象和接收及执行请求的对象区分开来。

迭代器模式(Iterator)

  • 提供了一种顺序访问聚合对象(也就是一个集合)中的元素,而又不必暴露潜在表示的方法。迭代器模式将访问和遍历集合元素的责任从集合对象转移到迭代器对象。迭代器定义一个访问集合元素的接口,并对当前元素进行跟踪。不同的迭代器可以执行不同的遍历策略。

状态模式(State)

  • 一个对象具有多种状态,其某种复杂行为取决于状态及状态变迁。此时,可以单独将状态及行为抽离出来建模,并将对象的行为委托状态对象完成。若状态行为比较简单,可以使用具有行为的枚举或表来实现;若状态行为比较复杂,则需要使用状态抽象类及具体状态子类来实现。状态对象可持有对象的引用,便于访问所需要的信息。状态子类没有实例属性,可以实现为单例类。

中介者模式(Mediator)

  • 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  • 有多个基础服务方,多个业务方;每个业务方都有自己需要定制化的东西。如果所有业务方直接与各个基础服务方打交道,会导致”服务方-业务方”的依赖关系激增,交互也变得复杂。引入中介者对象作为服务方和业务方的调度调控,每个基础服务方和业务方只需要与中介者交互,而不需要知道具体的服务方和业务方。中介者对象将交互行为集中化,简化了依赖关系数量。

解释器模式(Interpret)

  • 如果特定问题可以使用一种简单的语言来描述,那么可以定义一种文法,以及对应的解释器。将问题的求解归结为根据文法构造句子,然后使用解释器来解释句子生成结果。适用于领域语言设计和实现。

访问模式(Visitor)

  • 一个复杂的复合对象含有多个子对象,对于每个子对象都需要定义操作,而每个子对象的操作都可能不一样。全部操作的定义放在这个复合对象里,将使得这个复合对象异常的庞大臃肿。使用访问器模式,可以将这些操作抽离出来。每种操作都是一个访问器,可以定义访问器接口;每个子对象以及该复合对象都有一个accept方法。复合对象的accept实现将委托给遍历子对象的accept。Visitor主要用于为复合对象定义可扩展的新操作。当定义一个新操作时,只需要增加一个新的Visitor,而不需要改变现有的其他类。

备忘录模式(Memento)

  • 历史状态存储、撤销与回退。
  • 这种模式在不破坏封装的情况下,捕捉和外部化对象的内部状态,使对象在之后可以恢复到该状态。备忘录模式使关键对象的重要状态外部化,同时保持对象的内聚性。

最后更新: 2019年08月29日 16:33

原始链接: freesdw.github.io/2019/08/28/设计模式/

× 请我吃巧克力吧
打赏二维码