Design Pattern (2) 设计模式(2) Institute of Computer Software 2018/9/21 Nanjing University 2018/9/21
摘要 Design Patterns Why, What, How Creational, Structural and Behavioral Patterns Institute of Computer Software Nanjing University 2018/9/21
Structural Patterns 结构模式描述如何将类或者对象结合在一起形 成更大的结构。 类的结构模式:结构型类模式使用继承机制来组合 接口或实现。 对象的结构模式:结构型对象模式描述了如何对一 些对象进行组合,从而实现新功能的一些方法。可 以在运行时刻改变对象组合关系。 Institute of Computer Software Nanjing University 2018/9/21
Structural Patterns Adapter Bridge Composite Decorator Facade Flyweight Proxy Institute of Computer Software Nanjing University 2018/9/21
Adapter Aliases:Wrapper Intent Motivation 有时为复用而设计的工具箱类不能够被复用的原因 仅仅是因为它的接口与专业应用领域所需要的接口 不匹配 Institute of Computer Software Nanjing University 2018/9/21
Example target adaptee adapter Institute of Computer Software Nanjing University 2018/9/21
Applicability Use the Adapter pattern when 你想使用一个已经存在的类,而它的接口不符合你 的需求。 你想创建一个可以复用的类,该类可以与其它不相 关的类或不可预见的类(即那些接口可能不一定兼 容的类)协同工作。 (仅适用于对象Adapter)你想使用一些已经存在 的类,但是不可能对每一个都进行子类化以匹配它 们的接口。对象适配器可以适配它的父类接口。 Institute of Computer Software Nanjing University 2018/9/21
Structure 类适配器 对象适配器 Institute of Computer Software 2018/9/21 Nanjing University 2018/9/21
Participants Target Client Adaptee Adapter 定义Client使用的与特定领域相关的接口 定义一个已经存在的接口,这个接口需要适配 Adapter 对Adaptee的接口与Target的接口进行适配 Institute of Computer Software Nanjing University 2018/9/21
Collaboration Client在Adapter实例上调用一些操作,接着适配 器调用Adaptee的操作实现这个请求。 Sample Code Institute of Computer Software Nanjing University 2018/9/21
Consequences Adapter的匹配精度:Adapter的工作量取决于 Target接口与Adaptee接口的相似程度 本质:转换匹配,复用功能! 两种重用模型 class adapter: 无法适配adaptee以及所有它的子类,但是可以重 载adaptee的行为 object adapter 可以适配adaptee的所有子类,但重定义adaptee的 行为比较困难 Adapter的匹配精度:Adapter的工作量取决于 Target接口与Adaptee接口的相似程度 Institute of Computer Software Nanjing University 2018/9/21
Consequences 使用双向适配器提供透明操作 针对class adapter,用多重继承来实现 Institute of Computer Software Nanjing University 2018/9/21
Implementation 使用继承机制实现class adapter 使用内嵌对象技术实现object adapter Pluggable Adapters 使用抽象操作 使用代理对象 参数化的适配器:reflection技术 Institute of Computer Software Nanjing University 2018/9/21
Related Patterns Bridge:结构类似,但Bridge的目的是将接口部 分和实现部分分离,从而使得它们可以较为容 易也相对独立的加以改变。而adapter则意味着 改变一个已有对象的接口 Decorator:增强了其它对象的功能而同时又不 改变它的接口,透明性比adapter好 Proxy:在不改变它的接口的条件下,为另一个 对象定义了一个代理。 Institute of Computer Software Nanjing University 2018/9/21
增:Default Adapter 适配器模式的特例! 缺省适配模式为一个接口提供缺省实现,这样 子类型可以从这个缺省实现进行扩展,而不必 从原有接口扩展。 Motivation Java中有着特殊的应用 java.awt.event.WindowAdapter Institute of Computer Software Nanjing University 2018/9/21
Example Adapter! Compilation Passed! Compilation Error! abstract class 天星 implements和尚{ public void 吃斋(){}; public void 念经(){}; public void 打坐(){}; public void 撞钟(){}; public void 习武(){}; public String getName(){return null}; } interface 和尚{ public void 吃斋(); public void 念经(); public void 打坐(); public void 撞钟(); public void 习武(); public String getName(); } Adapter! class 鲁智深 extends 天星{ public void 习武(){ 拳打镇关西;大闹五台山;…} public String getName(){ return “鲁智深”;} } class 鲁智深 implements 和尚{ public void 习武(){ 拳打镇关西;大闹五台山;…} public String getName(){ return “鲁智深”;} } Compilation Passed! Compilation Error! Institute of Computer Software Nanjing University 2018/9/21
Applicability 如果不准备实现一个接口的所有方法时,可以 制造一个抽象类,给出所有方法的平庸的具体 实现 适配器模式把一个类的接口变换成客户端所期 待的另一种接口,适配器模式的“平庸化”形式可 以使所考察的类不必实现不需要的那部分接口。 Institute of Computer Software Nanjing University 2018/9/21
Structure ServiceAdapter应当是抽象类,但它所提供的方法却应当是具体的方法,而不是抽象的方法—提供默认实现。 具体子类可以按照需要只实现需要实现的方法,忽略不需要实现的方法。 Sample Code 2018/9/21 Institute of Computer Software Nanjing University
Bridge Aliases:Handle/Body Intent Motivation 回顾下OCP和CARP原则… 将抽象部分与它的实现部分分离,使它们都可以独立地变化 Motivation 要做到“抽象(接口)与实现分离”,最常用的办法是定义一个抽 象类,然后在子类中提供实现。也就是说,用继承机制达到 “抽象(接口)与实现分离” 但是这种方法不够灵活,继承机制把实现与抽象部分永久地 绑定起来,要想独立地修改、扩展、重用抽象(接口)与实现 都非常困难。 回顾下OCP和CARP原则… Institute of Computer Software Nanjing University 2018/9/21
扩展Window抽象使之用于不同种类的窗口或新的平台很不方便 Example 扩展Window抽象使之用于不同种类的窗口或新的平台很不方便 继承机制使得客户代码与平台相关 Institute of Computer Software Nanjing University 2018/9/21
将Window抽象和它的实现部分分别放在独立的类层次结构中 Example 将Window抽象和它的实现部分分别放在独立的类层次结构中 针对窗口接口 针对平台的窗口实现 Institute of Computer Software Nanjing University 2018/9/21
对“变化”的封装 Bridge! 需要变化 需要变化 抽象化 实现化 抽象化 实现化 实现化2 实现化3 抽象化 修正2 修正1 实现化 Abstraction Implementation 抽象化 实现化 Bridge! 需要变化 需要变化 抽象化 实现化 Abstraction Implementation 实现化2 实现化3 抽象化 Abstraction 修正2 修正1 实现化 Implementation 具体 实现化1 实现化2 Institute of Computer Software Nanjing University 2018/9/21
Applicability 编译时刻无法确定抽象(接口)与实现之间的关系 抽象部分与实现部分都可以通过子类化而扩展 对一个实现的修改不影响客户(无须重新编译) 在C++中,对客户完全隐瞒实现细节 因为扩展的原因,需要把一个类分成两部分, (以便灵活组合) 在多个对象之间共享数据,但客户不需要知道 Institute of Computer Software Nanjing University 2018/9/21
Structure Institute of Computer Software Nanjing University 2018/9/21
Participants Abstraction RefinedAbstraction Implementor 定义抽象类的接口 维护一个指向Implementor类型对象的指针 RefinedAbstraction 扩充由Abstraction定义的接口 Implementor 定义实现类的接口,不一定要与Abstraction的接口完全一致, 甚至可以完全不同 ConcreteImplementor 实现Implementor接口并定义它的具体实现 Institute of Computer Software Nanjing University 2018/9/21
Collaborations Abstraction将Client的请求转发给它的Implementor 对象 Sample Code Institute of Computer Software Nanjing University 2018/9/21
Consequences 本质:分离抽象和实现! 分离接口及其实现部分。抽象类的实现可以在 运行时刻进行配置,一个对象甚至可以在运行 时刻改变它的实现 提高可扩充性:抽象与实现两部分可以单独扩 充 实现细节对客户透明 Institute of Computer Software Nanjing University 2018/9/21
Implementation 仅有一个Implementor则没有必要创建一个抽象 的Implementor -- 退化 根据客户环境,或者通过factory (回顾Abstract Factory) 共享implementors 资源管理:引用计数技术 采用多继承机制,依赖于静态继承, 不好 Institute of Computer Software Nanjing University 2018/9/21
Related Patterns Abstract Factory可以用来创建和配置Bridge模式 与Adapter模式的区别 思考题:JDBC/ODBC桥梁是Bridge模式吗? Institute of Computer Software Nanjing University 2018/9/21
Composite Intent Motivation 一些部件对象经过组合构成的复合部件对象仍然具有单 个部件对象的接口,这样的复合部件对象被称为“容器 (container)” 复合部件与单个部件具有同样的接口,所有接口包含两 部分:单个部件的功能、管理子部件的功能 递归组合 Institute of Computer Software Nanjing University 2018/9/21
Example Institute of Computer Software Nanjing University 2018/9/21
Applicability 想表示对象的“部分-整体”层次结构 希望用户忽略组合对象与单个对象的不同,用 户将统一地使用组合结构中的所有对象 Institute of Computer Software Nanjing University 2018/9/21
Structure Institute of Computer Software Nanjing University 2018/9/21
Participants Component Leaf Composite Client 为组合中的对象声明接口 在适当的情况下实现所有类共有接口的缺省行为 声明一个接口用于访问和管理Component的子组件 (可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下 实现它。 Leaf 在组合中表示叶节点对象,没有子节点 在组合中定义图元对象的行为 Composite 定义有子部件的那些部件的行为 存储子部件 在Component接口中实现与子部件有关的操作 Client 通过Component接口操纵组合部件的对象 Institute of Computer Software Nanjing University 2018/9/21
Collaborations 用户使用Component类接口与组合结构中的对象 进行交互。如果接收者是一个叶节点,则直接 处理请求。如果接收者是Composite,它通常将 请求发送给它的子部件,在转发请求之前与/或 之后可能执行一些辅助操作。 Sample Code Institute of Computer Software Nanjing University 2018/9/21
Consequences 本质:统一leaf对象和composite对象! 简化客户代码,客户一致地处理复合对象和单个对象 使得更容易增加新类型的组件,易于增加新类型的组 件 使设计变得更加一般化,无法限制类型的组合,可以 在运行时刻通过类型检查加以弥补 Institute of Computer Software Nanjing University 2018/9/21
Implementation 显式的父对象的引用,在子对象中给出父对象的引用, 可以很容易地遍历所有的父对象 共享组件:当一个组件只有一个父部件时,很难共享 最大化Component接口? 声明管理子部件的操作:在安全性和透明性之间权衡 Component是否应该实现一个Component列表? 子部件排序 使用高速缓存存储改善性能 应该由谁删除Component 存储组件最好用哪一种数据结构: List, Array, HashMap? Institute of Computer Software Nanjing University 2018/9/21
Related Patterns Decorator经常和Composite一起使用,通常有一个 公共的父类 Flyweight可以共享组件但是不再能引用他们的父 类对象 Iterator:可用来遍历Composite Visitor:将本来应该分布在Composite和Leaf类中的 操作和行为局部化 思考题:考虑如何实现安全方式的Composite Institute of Computer Software Nanjing University 2018/9/21
Decorator Aliases: Wrapper Intent 动机 希望给某个对象而不是整个类添加一些功能 Institute of Computer Software Nanjing University 2018/9/21
Example Institute of Computer Software Nanjing University 2018/9/21
Example Institute of Computer Software Nanjing University 2018/9/21
Applicability 在不影响其他对象的情况下,以动态、透明的 方式给单个对象添加职责 处理那些可以撤销的职责 当不能采用生成子类的方法进行扩充时 可能有大量独立的扩展,为支持每一种组合将产生 大量的子类 类定义被隐藏或者类定义不能用于生成子类 Institute of Computer Software Nanjing University 2018/9/21
Structure Institute of Computer Software Nanjing University 2018/9/21
Participants Component ConcreteComponent Decorator ConcreteDecorator 定义一个对象接口,可以给这些对象动态地添加职责 ConcreteComponent 定义一个对象,可以给这个对象添加一些职责 Decorator 维持一个指向Component对象的指针,并定义一个与 Component接口一致的接口 ConcreteDecorator 向组件添加职责 Institute of Computer Software Nanjing University 2018/9/21
Collaboration Decorator将请求转发给它的Component对象,并 有可能在转发请求前后执行一些附加的动作。 Sample Code New Decorator1( new Decorator2 ( new Decorator3 ( new ConcreteComponent()))) 思考题:查看Java IO包,思考其中所使用的设计模式 Decorator1 包裹图 Decorator2 Decorator3 ConcreteComponent Institute of Computer Software Nanjing University 2018/9/21
Consequence 本质:动态组合! 优点 缺点 比静态继承更灵活 避免在层次结构高层的类有太多的特征 Decorator与它的Component不一样,使用装饰时不 应该依赖对象标识 有许多小对象 Institute of Computer Software Nanjing University 2018/9/21
Implementation 接口的一致性,装饰对象的接口必须与它所装 饰的Component的接口是一致的 当仅需要添加一个职责时可以省略抽象的 Decorator类 保持Component类的简单性 改变对象外壳与改变对象内壳的区别,结合 Strategy模式 Institute of Computer Software Nanjing University 2018/9/21
Related Patterns Adapter:Decorator模式中装饰仅改变对象的职 责而不改变它的接口,而Adapter模式中适配器 将给对象一个全新的接口 Composite:可以将装饰视为一个退化的仅有一 个组件的组合,Decorator的目的不在于对象聚 集 Strategy:用一个装饰可以改变对象的外表,而 Strategy模式可以改变对象的内核 Institute of Computer Software Nanjing University 2018/9/21
Facade Intent Motivation Institute of Computer Software Nanjing University 2018/9/21
Example Institute of Computer Software Nanjing University 2018/9/21
Applicability 为一个复杂的子系统提供一个简单接口。 子系统往往会非常复杂,但是其接口应该尽可 能地简单,特别是对于一般用户而言 为一个复杂的子系统提供一个简单接口。 子系统往往会非常复杂,但是其接口应该尽可 能地简单,特别是对于一般用户而言 客户与实现抽象功能的诸类之间存在一定的依 赖性,facade可以降低这种依赖性 在多个子系统的结构中,使用facade模式定义 子系统的入口点,有助于降低各子系统之间的 依赖性 Institute of Computer Software Nanjing University 2018/9/21
Structure Institute of Computer Software Nanjing University 2018/9/21
Participants Facade Subsystem classes 知道哪些子系统类负责处理请求 将客户的请求代理给适当的子系统对象 实现子系统的功能 处理由Facade对象指派的任务 没有facade的任何相关信息;即没有指向facade的 指针 Institute of Computer Software Nanjing University 2018/9/21
Collaborations 客户程序通过发送请求给Facade的方式与子系 统通讯,Facade将这些消息转发给适当的子系 统对象 Sample Code Institute of Computer Software Nanjing University 2018/9/21
Consequences 本质:封装交互,简化调用! 它对客户屏蔽子系统组件,因而减少了客户处 理的对象的数目并使得子系统使用起来更加方 便(回顾LoD原则) 它实现了子系统与客户之间的松耦合关系,而 子系统内部的功能组件往往是紧耦合的。 化“紧耦合”为“松耦合” —— 实现组件软件的关键技 术 并不限制应用使用子系统类 Institute of Computer Software Nanjing University 2018/9/21
Implementation 降低客户-子系统之间的耦合度,用抽象类实现 Facade而它的具体子类对应于不同的子系统实 现可以进一步解耦;另一种方法是用不同的子 系统对象配置Facade对象 公共子系统类与私有子系统类 Institute of Computer Software Nanjing University 2018/9/21
Related Patterns Abstract Factory模式可以与Facade模式一起使用 以提供一个接口,这一接口可用来以一种子系 统独立的方式创建子系统对象 Mediator模式与Facade模式的相似之处是,它抽 象了一些已有的类的功能 通常仅需要一个Facade对象,因此可以用 Singleton模式 Institute of Computer Software Nanjing University 2018/9/21
Proxy Aliases:Surrogate Intent Motivation 为其它对象提供一种代理以控制对这个对象的访问 对于每一个开销很大的对象,应该根据需要进行创 建 Institute of Computer Software Nanjing University 2018/9/21
Example Institute of Computer Software Nanjing University 2018/9/21
Applicability 在需要用比较通用和复杂的对象指针代替简单 的指针时使用Proxy模式 Remote Proxy:为一个对象在不同的地址空间提供 局部代表 Virtual Proxy:根据需要创建开销很大的对象 Protection Proxy:控制对原始对象的访问,用于对 象应该有不同访问权限的时候 Smart Reference:取代了简单的指针,在访问对象 时执行一些附加操作—引用计数,加锁,将第一次 引用的持久对象装入内存… Institute of Computer Software Nanjing University 2018/9/21
Structure 不改变主题接口,不让客户端感觉到代理的存在 传递请求之前/之后都可以执行特定的操作,而不是单纯传递请求 将客户端的调用委派给真实的主题对象 Institute of Computer Software Nanjing University 2018/9/21
Participants Proxy Subject RealSubject 保存一个引用使得proxy可以访问实体 控制对实体的存取,并可能负责创建和删除它 Subject 定义RealSubject和Proxy的共同接口 RealSubject 定义Proxy所代表的实体 Institute of Computer Software Nanjing University 2018/9/21
Collaboration Proxy根据其种类,在适当的时候向RealSubject 转发请求 Sample Code Institute of Computer Software Nanjing University 2018/9/21
Consequences 本质:控制对象访问! Proxy模式在访问对象时引入了一定程度的间接 性 Remote proxy可以隐藏一个对象存在于不同地址空 间的事实 Virtual proxy可以进行最优化 Protection Proxy和Smart Reference都允许在访问一个 对象时有一些附加的内务处理 Institute of Computer Software Nanjing University 2018/9/21
Implementation Java Dynamic Proxy java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问 Java 动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其对象 Institute of Computer Software Nanjing University java.lang.reflect.Proxy 2018/9/21
Implementation 如何使用 Java 动态代理。具体有如下四步骤: // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分 //派转发 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通过反射从生成的类对象获得构造函数对象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通过构造函数对象创建动态代理类实例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler }); Institute of Computer Software Nanjing University 2018/9/21
Implementation 简化的动态代理对象创建过程: // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从 //代理类到委托类的分派转发 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler ); Institute of Computer Software Nanjing University 2018/9/21
Related Patterns Adapter模式中适配器为它所适配的对象提供了 一个不同的接口,而Proxy则提供了与它的实体 相同的接口,或其接口的子集(Protection Proxy可 能会拒绝执行实体的操作) Decorator的实现和Proxy类似,但目的不同: Decorator对象添加一个或多个功能,而Proxy则 控制对对象的访问 Institute of Computer Software Nanjing University 2018/9/21
Flyweight Intent Motivation 运用共享技术有效支持大量细粒度的对象。 当对象的粒度太小的时候,大量对象将会产生巨大的资源消 耗,因此考虑用共享对象(flyweight)来实现逻辑上的大量对象。 Flyweight对象可用于不同的context中,本身固有的状态(内部 状态)不随context发生变化,而其他的状态(外部状态)随 context而变化 Flyweight模式对那些通常因为数量太大而难以用对象来表示 的概念或实体进行建模 Institute of Computer Software Nanjing University 2018/9/21
Example Institute of Computer Software Nanjing University 2018/9/21
Example 外部状态,作为参量传入 Flyweight对象 Institute of Computer Software Nanjing University 2018/9/21
Applicability 一个应用程序使用了大量的对象 完全由于使用大量的对象,造成很大的存储开 销 对象的大多数状态都可变为外部状态 如果删除对象的外部状态,那么可以用相对较 少的共享对象取代很多组对象 应用程序不依赖于对象标识 Institute of Computer Software Nanjing University 2018/9/21
Structure Institute of Computer Software Nanjing University 2018/9/21
Structure Object graph Sample Code Institute of Computer Software Nanjing University 2018/9/21
Participants Flyweight ConcreteFlyweight UnsharedConcreteFlyweight 不强制共享 FlyweightFactory 创建并管理flyweight对象 确保合理地共享flyweight,提供已创建的flyweight实例或者创建一 个(如果不存在的话) Client 维持一个对flyweight的引用 计算或存储一个(多个)flyweight的外部状态 Institute of Computer Software Nanjing University 2018/9/21
Collaboration Flyweight执行时所需的状态必定是内部或外部 的。内部状态存储在ConcreteFlyweight中,外部 对象则由Client对象存储或计算。当用户调用 flyweight对象的操作时,将该状态传递给它 用户不应直接对ConcreteFlyweight类进行实例化, 而只能从FlyweightFactory对象得到 ConcreteFlyweight对象,以保证对它们适当地进 行共享 Institute of Computer Software Nanjing University 2018/9/21
Consequences 本质:分离与共享! 把对象的状态分开:intrinsic and extrinsic 分离变与不变,共享不变的部分 节约存储空间:内部状态的共享节约了大量空 间,外部状态可通过计算获得从而进一步节约 空间 Flyweight与Composite结合,形成一个共享叶节 点的图。 Institute of Computer Software Nanjing University 2018/9/21
Implementation 删除外部状态:理想情况是,外部状态可以由 一个单独的对象结构计算得到,且该结构的存 储要求非常小 管理共享对象,客户不能直接实例化flyweight, 必须通过管理器,例如FlyweightFactory。 Institute of Computer Software Nanjing University 2018/9/21
Related Patterns 与Composite模式结合起来,用共享叶节点的有 向无环图实现一个逻辑上的层次结构 最好用Flyweight实现State和Strategy对象 Institute of Computer Software Nanjing University 2018/9/21
Summary:Structural Patterns Adapter 、Bridge、Facade Adapter用于两个已有的不兼容接口之间的转接 Bridge用于将一个抽象与多个可能的实现进行桥接 Facade用于为复杂的子系统定义一个新的简单易用的接口 Composite、Decorator和Proxy Composite用于构造对象(递归)组合结构 Decorator用于为对象增加新的职责 Proxy为目标对象提供一个替代者 Flyweight 针对细粒度对象的一种全局控制手段 Institute of Computer Software Nanjing University 2018/9/21
作业 比较Adapter,Bridge和Facade的相似点与不同 点 比较Composite,Decorator和Proxy的相似点与不 同点 如何在Composite模式中避免环状引用? 学习Java2.0对代理模式的支持—介绍 java.lang.reflect.Proxy, java.lang.reflect.InvocationHandler的使用 Institute of Computer Software Nanjing University 2018/9/21