# designer **Repository Path**: suny95/designer ## Basic Information - **Project Name**: designer - **Description**: 设计模式学习 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2019-12-28 - **Last Updated**: 2022-01-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # designer ## 设计模式学习 **为什么要学习设计模式?** + 写出优雅的代码 + 更好地重构项目 + 经典框架都在用设计模式解决问题 **软件设计原则** + 开闭原则 + 对扩展开放,对修改关闭 + 单一职责原则 + 一个类、接口、方法,只做一件事,保证功能的单一性和纯洁性 + 依赖倒置原则 + 通过抽象、接口,使得各类或模块相互之间不影响,实现松耦合 + 接口隔离原则 + 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口 + 迪米特法则 + 又叫最少知道原则,一个类对其所依赖的类,知道的越少越好(也就是控制好修饰符、控制好类的引用) + 里氏替换原则 + 子类可以扩展父类的功能,但是不能改变父类原有的功能 + 合成复用原则 + 尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的 #### 工厂模式(Factory pattern) **简单工厂模式** + 定义 + 是指由一个工厂对象决定创建出哪一种产品类的实例。属于创建型模式,但它不属于GOF,23种设计模式 + 优点 + 只需传入一个正确的参数,就可以获取你所需要的对象。无须知道其创建的细节。 + 缺点 + 工厂类的职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则。不易于扩展过于复杂的产品结构。 + 适用场景 + 工厂类负责创建的对象较少。客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。 **工厂方法模式** + 定义 + 是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行 + 属于创建型设计模式 + 优点 + 用户只需关心所需产品对应的工厂,无须关心创建细节。加入新产品符合开闭原则,提高了系统的可扩展性。 + 缺点 + 类的个数容易过多,增加了代码结构的复杂度。增加了系统的抽象性和理解难度。 + 适用场景 + 创建对象需要大量重复的代码。客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。一个类通过其子类来指定创建哪个对象。 **抽象工厂模式** + 定义 + 是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。属于创建型设计模式 + 优点 + 具体产品在应用层代码隔离,无须关心创建细节,将一个系列的产品族统一到一起创建。 + 缺点 + 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。增加了系统的抽象性和理解难度。 + 适用场景 + 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。 + 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。 + 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。 #### 原型模式(Prototype Pattern) + 定义 + 是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 + 优点 + 原型模式性能比直接new一个对象性能高,简化了创建过程 + 缺点 + 必须配备克隆(或者可拷贝)方法对克隆复杂对象或对克隆出的对象进行复杂改造时,易带来风险。深拷贝、浅拷贝要运用得当 + 适用场景 + 类初始化消耗资源较多。 + new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等) + 构造函数比较复杂。 + 循环体中生产大量对象时。 #### 建造者模式(Builder Pattern) + 定义 + 是将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示 + 使用建造者模式对于用户而言只需指定需要建造的类型就可以获得对象,建造过程及细节不需要了解 + 属于创建型模式 + 优点 + 1、封装性好,创建和使用分离。 + 2、扩展性好,建造类之间独立、一定程度上解耦。 + 缺点 + 1、产生多余的 Builder 对象。 + 2、产品内部发生变化,建造者都要修改,成本较大。 + 适用场景 + 建造者模式适用于一个具有较多的零件的复杂产品的创建过程,由于需求的变化,组成这个复杂产品的各个零件经常猛烈变化,但是它们的组合方式却相对稳定。 + 相同的方法,不同的执行顺序,产生不同的结果时。 + 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。 + 产品类非常复杂,或者产品类中的调用顺序不同产生不同的作用。 + 当初始化一个对象特别复杂,参数多,而且很多参数都具有默认值时。 #### 单例模式(Singleton Pattern) + 定义 + 单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。 隐藏其所有的构造方法。 + 属于创建型模式。 + 单例模式的优点 + 在内存中只有一个实例,减少了内存开销。 + 可以避免对资源的多重占用。 + 设置全局访问点,严格控制访问。 + 单例模式的缺点 + 没有接口,扩展困难。 + 如果要扩展单例对象,只有修改代码,没有其他途径。 + 单例模式的适用场景 + 确保任何情况下都绝对只有一个实例。 + 例如:ServletContext、ServletConfig、ApplicationContext、DBPool + 单例模式需要的实现的功能 + 私有化构造器 + 保证线程安全 + 延迟加载 + 防止序列化和反序列化破坏单例 + 防御反射攻击单例 #### 代理模式(Proxy Pattern) + 定义 + 代理模式是指为其他对象提供一种代理,以控制对这个对象的访问。 + 代理对象在客服端和目标对象之间起到中介作用。 + 属于结构型设计模式 + 优点 + 代理模式能将代理对象与真实被调用的目标对象分离。 + 一定程度上降低了系统的耦合程度,易于扩展。 + 代理可以起到保护目标对象的作用。 + 增强目标对象的职责 + 缺点 + 代理模式会造成系统设计中类的数目增加 + 在客户端和目标对象之间增加了一个代理对象,会造成请求处理速度变慢。 + 增加了系统的复杂度 + 代理模式的适用场景 + 保护目标对象 + 增强目标对象 **静态代理** + 定义 + 显式声明被代理对象 + 缺点 + 不符合开闭原则,被代理对象如果新增方法,代理类也需要新增方法 **JDK动态代理** + 定义 + 动态配置和替换被代理对象 + 实现原理 + 拿到被代理类的引用,并且获取它的所有的接口(反射获取)。 + JDK Proxy类重新生成一个新的类,实现了被代理类所有接口的方法。 + 动态生成Java代码,把增强逻辑加入到新生成代码中。 + 编译生成新的Java代码的class文件。 + 加载并重新运行新的class,得到类就是全新类。 **CGLib动态代理** + CGLib和JDK动态代理对比 + JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象。 + 2.JDK和CGLib都是在运行期生成字节码,JDK是直接写Class字节码,CGLib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。 + 3.JDK调用代理方法,是通过反射机制调用,CGLib是通过FastClass机制直接调用方法,CGLib执行效率更高。 **静态代理和动态的本质区别** + 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。 + 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。 + 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。 #### 委派模式(Delegate pattern) + 定义 + 委派模式(Delegate Pattern)的基本作用就是负责任务的调度和分配任务 + 跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理 + 代理模式注重过程,而委派模式注重结果 + 不属于GOF 23种设计模式之一 + 属于行为型模式 #### 策略模式(Strategy pattern) + 定义 + 策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互相替换 + 此模式让算法的变化不会影响到使用算法的用户 + 优点 + 策略模式符合开闭原则。 + 避免使用多重条件转移语句,如if...else...语句、switch语句 + 使用策略模式可以提高算法的保密性和安全性 + 缺点 + 客户端必须知道所有的策略,并且自行决定使用哪一个策略类。 + 代码中会产生非常多策略类,增加维护难度 + 适用场景 + 假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。 + 一个系统需要动态地在几种算法中选择一种 #### 模板模式(Template Pattern) + 定义 + 模板模式通常又叫模板方法模式(Template Method Pattern)是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现 + 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。 + 属于行为性设计模式。 + 优点 + 提高代码的复用性。 + 提高代码的扩展性。 + 符合开闭原则。 + 缺点 + 类数目的增加。 + 间接地增加了系统实现的复杂度。 + 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。 + 适用场景 + 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。 + 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。 #### 适配器模式(Adapter Pattern) + 定义 + 适配器模式是指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作。 + 适配器模式主要解决的是功能兼容问题。 + 属于结构型设计模式。 + 优点 + 能提高类的透明性和复用,现有的类复用但不需要改变。 + 目标类和适配器类解耦,提高程序的扩展性。 + 在很多业务场景中符合开闭原则。 + 缺点 + 适配器编写过程需要全面考虑,可能会增加系统的复杂性。 + 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。 + 适用场景 + 已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。 + 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。 #### 桥接模式(Bridge Pattern) + 定义 + 桥接模式也称为桥梁模式、接口模式或柄体(Handle and Body)模式,是将抽象部分与它的具体实现部分分离,使它们都可以独立变化 + 桥接模式主要目的是通过组合的方式建立两个类之间的联系,而不是继承 + 桥接模式的核心在于解耦抽象和实现 + 属于结构型模式 + 桥接模式包含 4 种角色 + 抽象(Abstraction):该类有一个对实现角色的引用,抽象角色中的方法需要实现角色来实现。抽象角色一般为抽象类(构造函数规定子类要传入一个实现对象) + 修正抽象(RefinedAbstraction):Abstract的具体实现,对Abstraction的方法进行完善和扩展,也可以没有这个角色 + 实现(Implementor):确定实现维度的基本操作,提供给Abstraction使用。该类一般为接口或抽象类 + 具体实现(ConcreteImplementor):Implementor的具体实现 + 适用场景 + 在抽象和具体实现之间需要增加更多的灵活性的场景 + 一个类存在两个(或多个)独立变化的维度,而这两个(或多个)维度都需要独立进行扩展 + 不希望使用继承,或因为多层继承导致系统类的个数剧增 + 优点 + 分离抽象部分及其具体实现部分 + 提高系统的扩展性 + 符合开闭原则 + 符合合成复用原则 + 缺点 + 增加了系统的理解与设计难度 + 需要正确地识别系统中两个独立变化的维度 #### 门面模式(Facade Pattern) + 定义 + 门面模式又叫外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用 + 属于结构性模式。 + 像我们常用的三层模型MVC, controller层就是一个门面模式, 对外提供统一接口, 其他功能都在内部实现, 不需要用户重复调用多次 + 门面模式主要包含 2 种角色 + 外观角色(Facade):也称 门面角色,系统对外的统一接口 + 子系统角色(SubSystem):可以同时有一个或多个 SubSystem。 + 每个 SubSystem 都不是一个单独的类,而是一个类的集合。 + SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言,Facade 只是另一个客户端而已(即 Facade 对 SubSystem 透明)。 + 优点 + 简化了调用过程,无需深入了解子系统,以防给子系统带来风险 + 减少系统依赖、松散耦合 + 更好地划分访问层次,提高了安全性 + 遵循迪米特法则,即最少知道原则 + 缺点 + 当增加子系统和扩展子系统行为时,可能容易带来未知风险 + 不符合开闭原则 + 某些情况下可能违背单一职责原则 #### 享元模式(Flyweight Pattern) + 定义 + 享元模式又称为轻量级模式,是对象池的一种实现。类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。 + 提供了减少对象数量从而改善应用所需的对象结构的方式。 + 其宗旨是共享细粒度对象,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗,属于结构型模式。 + 享元模式把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的 + 享元模式模式的本质是缓存共享对象,降低内存消耗。 + 享元模式有三个参与角色: + 抽象享元角色(Flyweight):享元对象抽象基类或者接口,同时定义出对象的外部状态和内部状态的接口或实现; + 具体享元角色(ConcreteFlyweight):实现抽象角色定义的业务。该角色的内部状态处理应该与环境无关,不能出现会有一个操作改变内部状态,同时修改了外部状态; + 享元工厂(FlyweightFactory):负责管理享元对象池和创建享元对象。 + 适用场景: + 常常应用于系统底层的开发,以便解决系统的性能问题。 + 系统有大量相似对象、需要缓冲池的场景。 + 优点 + 减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率; + 减少内存之外的其他资源占用。 + 缺点 + 关注内、外部状态、关注线程安全问题; + 使系统、程序的逻辑复杂化。 #### 组合模式(Composite Pattern) + 定义 + 组合模式(Composite Pattern)也称为整体-部分(Part-Whole)模式 + 它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性 + 属于结构型模式。 + 组合模式包含 3 个角色: + 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性; + 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构; + 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。 + 适用场景 + 希望客户端可以忽略组合对象与单个对象的差异时; + 对象层次具备整体和部分,呈树形结构。 + 优点 + 清楚地定义分层次的复杂对象,表示对象的全部或部分层次 + 让客户端忽略了层次的差异,方便对整个层次结构进行控制 + 简化客户端代码 + 符合开闭原则 + 缺点 + 限制类型时会较为复杂 + 使设计变得更加抽象 #### 装饰者模式(Decorator Pattern) + 定义 + 装饰器模式也称为包装模式(Wrapper Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能) + 装饰器模式的核心是功能扩展。使用装饰器模式可以透明且动态地扩展类的功能。 + 属于结构型模式。 + 优点 + 装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。 + 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。 + 装饰者完全遵守开闭原则。 + 缺点 + 会出现更多的代码,更多的类,增加程序复杂性。 + 动态装饰时,多层装饰时会更复杂。 + 适用场景 + 用于扩展一个类的功能或给一个类添加附加职责。 + 动态的给一个对象添加功能,这些功能可以再动态的撤销。 #### 装饰者模式和适配器模式对比 + 装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模 | | 装饰者模式 | 适配器模式 | | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 形式 | 是一种非常特别的适配器模式 | 没有层级关系,装饰器模式有层级关系 | | 定义 | 装饰者和被装饰者都实现同一个接 口,主要目的是为了扩展之后依旧保 留 OOP 关系 | 适配器和被适配者没有必然的联系,通 常是采用继承或代理的形式进行包装 | | 关系 | 满足 is-a 的关系 | 满足 has-a 的关系 | | 功能 | 注重覆盖、扩展 | 注重兼容、转换 | | 设计 | 前置考虑 | 后置考虑 | #### 观察者模式(Observer Pattern) + 定义 + 观察者模式又叫发布-订阅(Publish / Subscribe)模式、模型-视图(Model / View)模式、源-监听器(Source / Listener)模式或从属者(Dependents)模式。 + 定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。 + 属于行为型模式。 + 观察者模式主要包含4种角色 + 抽象主题(Subject):指被观察的对象(Observable)。该对象是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法。 + 具体主题(ConcreteSubject):具体被观察者,当其内部状态变化时,会通知已注册的观察者。 + 抽象观察者(Observe):定义了响应通知的更新方法。 + 具体观察者(ConcreteObserve):在得到状态更新时,会自动做出响应。 + 优点 + 观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则。 + 分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层上。 + 实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。 + 缺点 + 如果观察者数量过多,则事件通知会耗时较长。 + 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件。 + 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。 + 适用场景 + 观察者模式主要用于在关联行为之间建立一套触发机制的场景 #### 责任链模式(Chain of Responsibility Pattern) + 定义 + 责任链模式是将链中每个节点看作是个对象,每个节点处理的请求均不同,且内部自动生窗户个下一节点对象。 + 当一个请求从链式的首端发出肘,会沿着链的路径依次传递给每个节点对象,直至有对象处理这个请求为止。 + 属于行为型模式。 + 优点 + 将请求与处理解耦 + 请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级节点对象 + 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果 + 链路结构灵活,可以通过改变链路结构动态地新增或删减责任 + 易于扩展新的请求处理类(节点),符合开闭原则 + 缺点 + 责任链太长或者处理时间过长,会影响整体性能 + 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃 + 适用场景 + 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定 + 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求 + 可动态指定一组对象处理请求 #### 迭代器模式(Iterator Pattern) + 定义 + 迭代器模式又称游标模式(Cursor Pattern),它提供一种顺序访问集合/容器对象元素的方法,而又无需暴露集合内部表示。 + 迭代器模式可以为不同的容器提供一致的遍历行为,而不用关心容器内容元素组成结构。 + 迭代器模式的本质是抽离集合对象迭代行为到迭代器中,提供一致访问接口。 + 属于行为型模式。 + 优点 + 多态迭代:为不同的聚合结构提供一致的遍历接口,即一个迭代接口可以访问不同的集合对象。 + 简化集合对象接口:迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中,使集合对象无须关心具体迭代行为。 + 元素迭代功能多样化:每个集合对象都可以提供一个或多个不同的迭代器,使得同种元素聚合结构可以有不同的迭代行为。 + 解耦迭代与集合:迭代器模式封装了具体的迭代算法,迭代算法得变化不会影响到集合对象的结构 + 缺点 + 对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐。 + 适用场景 + 访问一个集合对象的内容而无需暴露它的内部表示。 + 为遍历不同的集合结构提供一个统一的访问接口。 #### 命令模式(Command Pattern) + 定义 + 命令模式是对命令的封装,每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。 + 命令模式解耦了请求放和接收方,请求放只需执行命令,不用关心命令是怎么被接收,怎么被操作以及是否被执行等。 + 命令模式通过为请求与实现间引入了一个抽象命令接口,解耦了请求与实现,并且中间件是抽象的,它可以有不同的子类实现,因此具备扩展性。 + 属于行为型模式。 + 命令模式主要包含四种角色 + 接收者角色(Receiver):该类负责具体实施或执行一个请求。 + 命令角色(Command):定义需要执行的所有命令行为。 + 具体命令角色(ConcreteCommand):该类内部维护一个接收者(Receiver),在其execute()方法中调用Receiver的相关方法。 + 请求者角色(Invoker):接收客户端的命令,并执行命令。 + 优点 + 通过引入中间件(抽象接口),解耦了命令请求与实现。 + 扩展性良好,可以很容易地增加新命令。 + 支持组合命令,支持命令队列。 + 可以在现有命令的基础上,增加额外操作(比如日志记录...,结合装饰器模式更好)。 + 缺点 + 具体命令类可能过多。 + 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口)。 + 增加了理解上的困难(不过这也是设计模式带来的一个通病,抽象必然引入额外类型,抽象肯定比紧密难理解)。 + 适用场景 + 现实语义中具备“命令”的操作(如命令菜单,shell命令...)。 + 请求调用者和请求的接收者需要解耦,使得调用者和接收者不直接交互。 + 需要抽象出等待执行的行为,比如撤消(Undo)操作和恢复(Redo)等操作。 + 需要支持命令宏(即命令组合操作)。 #### 状态模式(State Pattern) + 定义 + 状态模式也称为状态机(State Machine Pattern),是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。 + 状态模式中类的行为是由状态决定的,不同的状态下有不同的行为。其意图是让一个对象在其内部改变时,其行为也随之改变。 + 属于行为型模式 + 优点 + 结构清晰:将状态独立为类,消除了冗余的 if...else或switch...case 语句,使代码更加简洁,提高系统可维护性。 + 将状态转换显示化:通常的对象内部都是使用数值类型来定义状态,状态的切换是通过赋值进行表现,不够直观;而使用状态类,在切换状态时,是以不同的类进行表示,转换目的更加明确。 + 状态类职责明确且具备扩展性。 + 缺点 + 类膨胀:如果一个事物具备很多状态,则会造成状态类太多。 + 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 + 状态模式对开闭原则的支持不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源代码。 + 适用场景 + 行为随状态改变而改变的场景。 + 一个操作中含有庞大的多分支结构,并且这些分支取决于对象的状态。 #### 备忘录模式(Memento Pattern) + 定义 + 备忘录模式也称为快照模式(Snapshot Pattern)或令牌模式(Token Pattern),是指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。以后可将对象恢复到原先保存的状态。 + 备忘录模式本质是从发起人实体类(Originator)隔离存储功能,降低实体类的职责。 + 同时由于存储信息(Memento)独立,且存储信息的实体交由管理类(Caretaker)管理,则可以通过为管理类扩展额外的功能对存储信息进行扩展操作(比如增加历史快照功能....)。 + 属于行为型模式。 + 优点 + 简化发起人实体类(Originator)职责,隔离状态存储与获取,实现了信息的封装,客户端无需关系状态的保存细节。 + 提供状态回滚功能。 + 缺点 + 消耗资源:如果需要保存的状态过多时,每一次保存都会消耗很多内存。 + 适用场景 + 需要保存历史快照的场景。 + 希望在对象之外保存状态,且除了自己其他类对象无法访问状态保存具体内容。 #### 中介者模式(Mediator Pattern) + 定义 + 中介者模式又称为调解者模式或调停者模式。 + 用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 + 属于行为型模式。 + 中介者模式主要包含4个角色 + 抽象中介者(Mediator):定义统一的接口,用于各同事角色之间的通信。 + 具体中介者(ConcreteMediator):从具体的同事对象接收消息,向具体同事对象发出命令,协调各同事间的协作。 + 抽象同事类(Colleague):每一个同事对象均需要依赖中介者角色,与其他同事间通信时,交由中介者进行转发协作。 + 具体同事类(ConcreteColleague):负责实现自发行为(Self-Method),转发依赖方法(Dep-Method)交由中介者进行协调。 + 优点 + 减少类间依赖,将多对多依赖转化成一对多,降低了类间耦合。 + 类间各司其职,符合迪米特法则。 + 缺点 + 中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系,当同事类越多时,中介者就越臃肿,变得复杂且难以维护。 + 适用场景 + 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。 + 交互的公共行为,如果需要改变行为则可以增加新的中介者类。 #### 解释器模式(Interpreter Pattern) + 定义 + 解释器模式是指给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 + 解释器是一个简单语法分析工具,它可以识别句子语义,分离终结符号和非终结符合,提取出需要的信息,让我们能针对不同信息作出相应的处理。 + 是一种按照规定的语法(文法)进行解释的模式,其核心思想是识别文法,构建解释。 + 属于行为型模式。 + 解释器模式主要包含4个角色 + 抽象表达式(Expression):负责定义一个解释方法 interpret,交由具体子类进行具体解释。 + 终结符表达式(TerminalExpression):实现文法中与终结符有关的解释操作。文法中的每一个终结符都有一个具体终结表达式与之相对应。 + 比如公式 R = R1 + R2,R1 和 R2 就是终结符,对应的解析R1和R2的解释器就是终结符表达式。 + 通常一个解释器模式只有一个终结符表达式,但有多个实例,对应不同的终结符(R1,R2)。 + 非终结符表达式(NonTerminalExpression):实现文法中与非终结符有关的解释操作。 + 文法中的每条规则都对应于一个非终结符表达式。非终结符表达式一般是文法中的运算符或其他关键字。 + 比如公式 R = R1 + R2,“+” 就是非终结符,解析 “+” 的解释器就是一个非终结符表达式。 + 非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应于一个非终结符表达式。 + 上下文环境类(Context):包含解释器之外的全局信息。它的任务一般是用来存放文法中各个终结符所对应的具体值。 + 比如 R = R1 + R2,给 R1 赋值 100,给 R2 赋值 200,这些信息需要存放到环境中。 + 优点 + 扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可,若扩展语法时,只需添加相应非终结符类即可。 + 易于实现文法:解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式。 + 增加了新的解释表达式的方式。 + 缺点 + 语法规则较复杂时,会引起类膨胀:解释器模式每个语法都要产生一个非终结符表达式,当语法规则比较复杂时,就会产生大量的解释类,增加系统维护困难。 + 执行效率较低:解释器模式采用递归调用方法,每个非终结符表达式只关心与自己有关的表达式,每个表达式都需要指定最终的结果,因此完整表达式的最终结果是通过从后往前递归调用的方式获取得到。 当完整表达式层级较深时,解释效率下降,且出错时调试困难,因为递归迭代层级太深。 + 适用场景 + 一些重复出现的问题可以用一种简单的语言来进行表达。 + 一个简单语法需要解释的场景。 #### 访问者模式(Visitor Pattern) + 定义 + 访问者模式是一种将数据结构与数据操作分离的设计模式。 + 是指封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 + 访问者模式的核心是解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性。 + 可以通过扩展不同的数据操作类型(访问者)实现对相同元素集的不同的操作。 + 属于行为型模式。 + 优点 + 解耦了数据结构与数据操作,使得操作集合可以独立变化。 + 扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作。 + 元素具体类型并非单一,访问者均可操作。 + 各角色职责分离,符合单一职责原则。 + 缺点 + 无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进行,则访问者类必须增加对应元素类型的操作,违背了开闭原则。 + 具体元素变的更困难:具体元素增加属性,删除属性等操作会导致对应的访问者类需要进行相应的修改,尤其当有大量访问者类时,修改范围太大。 + 违背依赖倒置原则:为了达到“区别对待”,访问者依赖的是具体元素类型,而不是抽象。 + 适用场景 + 数据结构稳定,作用于数据结构的操作经常变化的场景。 + 需要数据结构与数据操作分离的场景。 + 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景。