HIT-SC-Chapter Eleven
HIT-SC-Chapter Eleven
Design Patterns for Reuse and
Maintainability
面向可复用性和可维护性的设计模式
1 Creational patterns
Factory Method pattern
Factory Method
-
Also known as “Virtual Constructor” 虚拟构造器
-
目的:
- 为创建对象定义一个接口,让子类决定初始化哪个类
- 工厂化模式将类初始化延伸至子类。
-
When should we use Factory Method? ---- When a class:
- 当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。
- 定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
-
- 常规情况下,client直接创建具体对象 Product p = new ProductTwo();
- 在工厂方法模式下:Product p = new ConcreteTwo().makeObject();
- Client需要在程序中直接实例化具体实现类,不是面向接口编程。
- Client使用“工厂方法”来创建实例,得到实例的类型是抽象接口而非具体类
- 静态工厂方法既可以在ADT内部实现,也可以构造单独的工厂类
-
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体
创建过程; - 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对
原工厂进行任何修改,满足开闭原则;
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体
-
缺点
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加
了系统的复杂度
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加
-
???
2 Structural patterns
(1) Adapter
加个“适配器”以便于复用
-
Adapter模式的意图:
- 将一个类的接口转换成客户希望的另外一个接口。
- 使原本由于接口不兼容而不能一起工作的类可以一起工作。
- 适配器模式分为类结构型模式和对象结构型模式两种
- 前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
-
优缺点:
-
Adapter Pattern 适配器模式
-
意图:将类的接口转换为客户端期望的另一个接口
-
将某个类/接口转换为client期望的其他形式
- 解决类之间接口不兼容的问题
- 为已有的类提供新的接口
- 适配器允许由于接口不兼容而无法正常工作的类协同工作
- 通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。
- to reuse an old component to a new system
-
目标:对旧的不兼容组件进行包装,在新系统中使用旧的组件
-
采用继承或Delegation,加个“适配器”以便于复用
-
-
(2) Decorator
- 指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
- 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
- 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
- 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
装饰模式的结构与实现
- 通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。下面来分析其基本结构和实现方法。
模式的结构
- 装饰模式主要包含以下角色。
- –抽象构件(Component)角色:
- 定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色:
- 实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:抽象类
- 继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色:
- 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
- –抽象构件(Component)角色:
模式的实现
Motivating example of Decorator pattern
-
问题: 需要对对象进行任意或者动态的扩展组合
-
方案: 实现一个通用接口作为要扩展的对象,将主要功能委托给基础对象(stack),然后添加功能(undo,secure,..)。
- 以递归的方式实现
- 比静态继承更灵活可定制的内聚扩展
- 装饰器同时使用子类型和委托
-
Component接口定义了操作,或者装饰器可以执行的公共操作
-
ConcreteComponent类是你可以动态添加特性的初始对象。您将首先创建这个对象并向其添加特性
-
Decorator类是一个抽象类,是所有Decorator的父类。虽然它实现了Component接口来定义操作,但它也包含一个受保护的变量Component,该变量指向要装饰的对象。
- The component variable is simply assigned in the constructor.
-
ConcreteDecorator类是能够添加特性的实际装饰类。你可以有任意多的ConcreteDecorator类,每个类都代表一个可以添加的特性。
-
-
Decorator vs. Inheritance
- Decorator在运行时合成特性
- 继承在编译时组合特性
- 装饰器由多个协作对象组成
- 继承生成一个类型明确的对象
- 可以混搭多种装饰
- 多重继承在概念上很困难
Decorators from java.util.Collections
- Turn a mutable list into an immutable list:
- static List
unmodifiableList(List lst); - static Set
unmodifiableSet( Set set); - static Map<K,V> unmodifiableMap( Map<K,V> map);
- static List
- Similar for synchronization:
- static List
synchronizedList(List lst); - static Set
synchronizedSet( Set set); - static Map<K,V> synchronizedMap( Map<K,V> map);
- static List
3 Behavioral patterns
(1) Strategy
整体地替换算法
Strategy Pattern
-
问题:
- 对于特定的任务存在不同的算法,但是客户端可以在运行时根据动态上下文在算法之间切换。
-
示例:对客户列表排序(冒泡排序、合并排序、快速排序)
-
定义一个算法的接口,每个算法用一个类来实现,客户端针对接口编写程序。
- 易于扩展的新算法实现
- 从客户端上下文分离算法
-
整体地替换算法
-
-
(2) Template Method
模板方法模式
-
Problem:
- 不同的客户端具有相同的算法步骤,但是每个步骤的具体实现不同。
-
示例:
- —执行测试用例的测试套件
- —打开、读取、写入不同类型的文档
-
Solution:
-
– The common steps of the algorithm are factored out into an abstract class, with abstract (unimplemented) primitive operations representing the customizable parts of the algorithm.在父类中定义通用逻辑和各步骤的抽象方法声明
-
– Subclasses provide different realizations for each of these steps.子类中进行各步骤的具体实现
-
-
模板方法模式适用性
- 在父类声明一个通用逻辑
- 模板模式用继承+重写的方式实现算法的不同部分
- 策略模式用委托机制实现不同完整算法的调用(接口+多态)
- Template Method is widely used in frameworks
(4) Visitor Pattern
-
对特定类型的object的特定操作(visit),在运行时将二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类
- 访问者模式实际做的是创建一个使用其他类中数据的外部类。
- 如果操作逻辑发生变化,那么我们只需要在visitor实现中进行更改,而不是在所有的item类中进行更改。
-
本质上:将数据和作用于数据上的某种/些特定操作分离开来。
-
为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下通过delegation接入ADT。
-
-
Visitor vs Iterator
- 迭代器:
- 以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能delegate到外部的iterator对象。
- Visitors:
- 特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT
Strategy vs visitor
- 二者都是通过delegation建立两个对象的动态联系
- 但是Visitor强调是的外部定义某种对ADT的操作,该操作于ADT自身关系不大(只是访问ADT),故ADT内部只需要开放accept(visitor)即可,client通过它设定visitor操作并在外部调用。
- 而Strategy则强调是对ADT内部某些要实现的功能的相应算法的灵活替换。这些算法是ADT功能的重要组成部分,只不过是delegate到外部strategy类而已。
- visitor是站在外部client的角度,灵活增加对ADT的各种不同操作(哪怕ADT没实现该操作),strategy则是站在内部ADT的角度,灵活变化对其内部功能的不同配置。
4 Commonality and Difference of Design Patterns
设计模式的对比:共性样式1
Adaptor
- 适用场合:你已经有了一个类,但其方法与目前client的需求不一致。
- 根据OCP原则,不能改这个类,所以扩展一个adaptor和一个统一接口。
Template
- 适用场合:有共性的算法流程,但算法各步骤有不同的实现典型的“将共性提升至超类型,将个性保留在子类型。
设计模式的对比:共性样式2
Strategy策略
- 根据OCP原则,想有多个算法的实现,在右侧树里扩展子类型即可,在左侧子类型里传入不同的类型实例
Iterator
Factory Method
- 左右两棵树的子类型一一对应。如果在工厂方法里使用type表征右侧的子类型,那么左侧的子类型只要1个即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理