第五次作业
行为型模式:Chain of Responsibility 职责链模式、Command 命令模式、Interpreter 解释器模式、Mediator 中介者模式、Iterator 迭代器模式、Observer 观察者模式、Chain Of Responsibility 职责链模式、Memento 备忘录模式、State 状态模式、Strategy 策略模式、Visitor 访问者模式
Template Method模式的几个要点
• Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
• 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
• 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法。
Command模式的几个要点
• Command模式的根本目的在于将“行为请求者”与“行为实现者” 解耦,在面向对象语言中,常见的实现手段是“将行 为抽象为对象”。
• 实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。
• 通过使用Composite模式,可以将多个“命令”封装为一个“复合命令”MacroCommand。
• Command模式与C#中的Delegate有些类似。但两者定义 行为接口的规范有所区别:Command以面向对象中的“接 口-实现”来定义行为接口规范,更严格,更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活, 但抽象能力比较弱。
Interpreter模式的几个要点
• Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
• 使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。
• Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interperter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。
Mediator模式的几个要点
• 将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变“多个对象互相关联”为“多个对象和一个中介者关联”,简化了系统的维护,抵御了可能的变化。
• 随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。
• Façade模式是解耦系统外到系统内(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。
Iterator模式的几个要点
• 迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
• 迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
• 迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
Observer模式的几个要点
• 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
• 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知,目标对象对此一无所知。
• 在C#的event中,委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。
Chain of Responsibility模式的几个要点
• Chain of Responsibility 模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
• 应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
• 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任
Memento模式的几个要点
• 备忘录(Memento)存储原发器(Originator)对 象的内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”
• 在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口;一个为其他对象使用的窄接口。
• 在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种 增量式改变来改进Memento模式。
State模式的几个要点
• State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
• 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换过来,要么不转换。
• 如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销。
Strategy模式的几个要点
• Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。所谓封装算法,支持算法的变化。
• Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
• 与State类似,如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
Visitor模式的几个要点
• Visitor模式通过所谓双重分发(double dispatch) 来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
• 所谓双重分发即 Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept 方法的多态辨析;第二个为visit方法的多态辨析。
• Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Vistor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”。
第四次作业结构型模式补:
结构型模式:Adapter 适配器模式、Bridge 桥接模式、Composite 组合模式、Decorator 装饰模式、Facade 外观模式、Flyweight 享元模式、Proxy 代理模式
Adapter模式的几个要点
• Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况” ,在遗留代码复用、类库迁移等方面非常有用。
• GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
• Adapter模式可以实现的非常灵活,不必拘泥于Gof23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。
• Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
Bridge模式的几个要点
• Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有 的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度来变化。
• 所谓抽象和实现沿着各自纬度的变化,即“子类化”它们,比如不同的Tank型号子类,和不同的平台子类)。得到各个子类之后,便可以任意组合它们,从而获得不同平台上的不同型号。
• Bridge模式有时候类似于多继承方案,但是多继承方案往往违 背单一职责原则(即一个类只有一个变化的原因),复用性比 较差。Bridge模式是比多继承方案更好的解决方法。
• Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使 有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。
Composite模式的几个要点
• Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一 对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
• 将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思 想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能“应对变化”。
• Composite模式中,是将“Add和Remove等和对象容器相关的方法”定 义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容 器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题, 需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.NET控件的实现在这方面为我们提供了一个很好的示范。
• Composite模式在具体实现中,可以让父对象中的子对象反向追溯; 如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率
Decorator模式的几个要点
• 通过采用组合、而非继承的手法, Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了 单独使用继承带来的“灵活性差”和“多子类衍生问题”。
• Component类在Decorator模式中充当抽象接口的角色,不应该去实 现具体的行为。而且Decorator类对于Component类应该透明——换 言之Component类无需知道Decorator类,Decorator类是从外部来扩 展Component类的功能。
• Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现 为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一 个Component对象,且装饰后的对象仍然是一个Component对象。
• Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义
Facade模式的几个要点
• 从客户程序的角度来看, Facade模式不仅简化了整个组件系统的接口,同时对于组件内部与外部客户程序来说,从某种程度上也达到了一种“解耦”的效果——内部子系统的任何变化不会影响到Façade接口的变化。
• Façade设计模式更注重从架构的层次去看整个系统,而不是单个类的层次。Façade很多时候更是一种架构设计模式。
• 注意区分Façade模式、Adapter模式、Bridge模式与Decorator模式。Façade模式注重简化接口,Adapter模式注重转换接口,Bridge模式注重分离接口(抽象)与其实现,Decorator模式注重稳定接口的前提下为对象扩展功能。
Flyweight模式的几个要点
• 面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
• Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
• 对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。
Proxy模式的几个要点
• “增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常用手段。
• 具体proxy设计模式的实现方法、实现粒度都相差 很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组件模块提供抽 象代理层,在架构层次对对象做proxy。
• Proxy并不一定要求保持接口的一致性,只要能够 实现间接控制,有时候损及一些透明性是可以接受的。