# DesignPattern **Repository Path**: zcrnhm/DesignPattern ## Basic Information - **Project Name**: DesignPattern - **Description**: 设计模式 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2022-07-17 - **Last Updated**: 2022-07-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计模式基础 ## 设计模式简介 软件工程中,**设计模式**(design pattern)是对软件中**普遍存在(反复出现)**的各种问题,所提出的解决方案,是由**Erich Gamma**、Richard Helm、Ralph Johnson 和 John Vlissides 四人在1990年代冲工程建筑领域引入计算机科学的 ## 设计模式解决问题 - 客户提出新功能 【使用设计模式软件具有更好的扩展性】 - 原公司程序员离职,项目接手【使用设计模式的代码更具有可维护性(可读性和规范性)】 ## 发展过程 面向对象(oo) --> 功能模块 【设计模式 + 算法】 --> 框架【多种设计模式】--> 架构【服务器集群】 ## 设计模式的目的 编写软件的过程中,程序面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性等多方面的挑战,设计模式用为让程序(软件)具有更好: - 代码重用性 - 可读性 - 可扩展性 - 可靠性 - 使程序呈现高内聚、低耦合的特性 ## 设计模式七大原则 - 单一职责原则 (Single Responsibility Principle) - 开放-关闭原则 (Open-Closed Principle) - 里氏替换原则 (Liskov Substitution Principle) - 依赖倒转原则 (Dependence Inversion Principle) - 接口隔离原则 (Interface Segregation Principle) - 迪米特法则(Law Of Demeter) - 组合/聚合复用原则 (Composite/Aggregate Reuse Principle) 详情解析→ [设计模式概念和七大原则](https://cloud.tencent.com/developer/article/1650116) `设计原则核心思想`: - 找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起 - 针对接口编程,而不是针对实现编程 - 为了交互对象之间的松耦合设计而努力 # UML类图 ## UML基本介绍 - UML(Unified modeling language) (统一建模语言), 是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果 - UML本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等,如下图: ![image-uml](uml/src/com/xy/uml.png) ## 依赖-dependency ```java public class PersonServiceBean { private PersonDao personDao; public void save(Person person) { } public IDCard getIDCard(Integer personId) { return null; } public void modify() { Department department = new Department(); } } ``` ![dependence](uml/src/com/xy/dependence/dependence.png) ## 泛化(继承)-Gerneralization ```java public abstract class DaoSupport { public void save(Object entity) { } public void delete(Object id) { } } ``` ```java public class PersonServiceBean extends DaoSupport{ } ``` ![generalization](uml/src/com/xy/generalization/generalization.png) ## 实现-Realization ```java public interface PersonService { public void delete(Integer id); } ``` ```java public class PersonServiceBean implements PersonService{ @Override public void delete(Integer id) { } } ``` ![realization](uml/src/com/xy/implementation/realization.png) ## 聚合-Aggregation `整体和部分可分割` ```java public class Computer { private Mouse mouse; private Monitor monitor; public void setMouse(Mouse mouse) { this.mouse = mouse; } public void setMonitor(Monitor monitor) { this.monitor = monitor; } } ``` ![aggregation](uml/src/com/xy/aggregation/aggregation.png) ## 组合-Composite `整体和部分不可分割` ```java public class Computer { private Mouse mouse = new Mouse(); private Monitor monitor = new Monitor(); } ``` ![composition](uml/src/com/xy/composition/composition.png) # 设计模式类型 设计模式可以分为三种类型 ● 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。 ● 结构型模式:把类或对象结合在一起形成一个更大的结构。 ● 行为型模式:类和对象如何交互,及划分责任和算法。 | 类型 | 所包含设计模式 | | :--------: | :----------------------------------------------------------: | | 创建型模式 | 单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式 | | 结构型模式 | 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 | | 行为型模式 | 模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、职责模式 | # 设计模式——单例模式 ## 单例设计模式介绍 - 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能**存在一个对 象实例,并且该类只提供一个取得其对象实例的方法(静态方法)**。 ## 单例设计模式八种方式 - 饿汉式(静态常量) - 饿汉式(静态代码块) - 懒汉式(线程不安全) - 懒汉式(线程安全, 同步方法) - 懒汉式(线程安全,同步代码块) - 双重检查 - 静态内部类 - 枚举 ### 饿汉式(静态常量) ```java class Singleton { /** * 1、构造器私有化 */ private Singleton(){} /** * 2、内部创建对象实例 */ private final static Singleton instance = new Singleton(); /** * 3、提供获取方法 */ public static Singleton getInstance() { return instance; } } ``` ### 饿汉式(静态代码块) ```java class Singleton { /** * 1、构造器私有化 */ private Singleton(){ } /** * 2、创建对象实例(去掉final) */ private static Singleton instance; /** * 3、静态代码块中创建单例对象 */ static { instance = new Singleton(); } /** * 4、提供获取方法 */ public static Singleton getInstance() { return instance; } } ``` 饿汉式优缺点说明: - `优点`:这种写法比较简单,就是在**类装载的时候就完成实例化**。避免了线程同步问题。 - `缺点`:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会**造成内存的浪费**。 - 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果。 - `结论`:这种单例模式可用,**可能造成内存浪费**。 ### 懒汉式(线程不安全) ```java class Singleton { private static Singleton instance; private Singleton(){} /** * 提供静态方法,当使用方法时,才去创建instance * @return */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 懒汉式(线程不安全)优缺点说明: - `优点`:**起到了 Lazy Loading的效果,但是只能在单线程下使用**。 - `缺点`:如果在多线程下, 一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。 - `结论`: 在实际开发中,**不要使用这种方式**。 ### 懒汉式(线程安全,同步方法) ```java class Singleton { private static Singleton instance; private Singleton() { } /** * 加入synchronized 实现线程安全 * @return */ public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` ### 懒汉式(线程安全,同步代码块) ```java class Singleton { private static Singleton instance; private Singleton() { } /** * 加入synchronized 实现线程安全 * @return */ public static Singleton getInstance() { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } } ``` 懒汉式(线程安全)优缺点说明: - `优点`:**解决了线程安全问题**。 - `缺点`:效率太低了,每个线程在想获得类的实例时候,执行getInstance(方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了,**方法进行同步效率太低**。 - `结论`:在实际开发中,**不推荐使用这种方式**。 ### 双重检查 ```java class Singleton { // 对象的创建可能发生指令的重排序 // 使用 volatile 可以禁止指令的重排序 // 保证多线程环境内的系统安全。 private static volatile Singleton instance; private Singleton() {} /** * 双重检查机制 * @return */ public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` 双重检查优缺点说明: - Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以**保证线程安全了**。 - 这样, 实例化代码只用执行一次,后面再次访问时,判断if (singleton == nul),直接return实例化对象,**也避免的反复进行方法同步**。 - 线程安全; **延迟加载;效率较高**。 - 结论:在实际开发中,**推荐使用这种单例设计模式**。 ### 静态内部类 ```java class Singleton { private Singleton(){} private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ return SingletonInstance.INSTANCE; } } ``` 静态内部类优缺点说明: - 这种方式采用了类装载的机制来**保证初始化实例时只有一 个线程**。 - 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。 - 类的静态属性只会在第一次加载类的时候初始化,所以在这里,**JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的**。 - 优点: **避免了线程不安全,利用静态内部类特点实现延迟加载,效率高**。 - 结论:**推荐使用**。 ### 枚举 ```java enum Singleton { TEST1(1,"test1"), TEST2(2,"test2"); private final Integer code; private final String msg; Singleton(Integer code, String msg) { this.code = code; this.msg = msg; } public Integer getCode() { return code; } public String getMsg() { return msg; } } ``` 枚举优缺点说明: - 这借助JDK1.5中添加的枚举来实现单例模式。不仅**能避免多线程同步问题,而且还能防止反序列化重新创建新的对象**。 - 这种方式是**Effective Java作者Josh Bloch提倡的方式**。 - 结论: **推荐使用**。 ## 单例模式注意事项和细节说明 - 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于- -些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。 - 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。 - 单例模式**使用的场景**:**需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多**(即:**重量级对象**),但又经常用到的对象、**工具类对象**、频繁访问数据库或文件的对象(比如**数据源**、session工厂等)。 # 设计模式--工厂模式 ## 简单工厂模式 **模式介绍:** 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。 该模式中包含的角色及其职责 - **工厂(Creator)角色** ​ 工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。 - **抽象产品(Product)角色** ​ 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。 - **具体产品(Concrete Product)角色** ​ 简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。 **案例:** 背景解析:有一家披萨店(PizzaStore),通过不同的平台(OrderPizza)点餐,用户可以选择喜欢的口味,通过简单工厂模式(SimpleFactory)将顾客的披萨进行生产,而披萨具有相同的特征(烘培、切割、装盒),可以抽象成相同的Pizza抽象类,使用简单工厂模式可以实现添加披萨类型时,披萨只需要继承基类,修改工厂类即可,无需其他代码的修改 ![simpleFactory](factory/src/com/xy/simplefactory/simpleFactory.png) `披萨抽象类` ```Java public abstract class Pizza { protected String name; public abstract void prepare(); public void cut() { System.out.println(name + ", 开始切割"); } public void box() { System.out.println(name + ", 开始装盒"); } public void bake() { System.out.println(name + ", 开始烘培"); } public void setName(String name) { this.name = name; } } ``` `各种披萨子类` ```java public class CheesePizza extends Pizza { @Override public void prepare() { System.out.println("制作奶酪披萨,准备原材料 "); setName("奶酪披萨"); } } ``` ```java public class GreekPizza extends Pizza { @Override public void prepare() { System.out.println("制作希腊披萨,准备原材料"); setName("希腊披萨"); } } ``` `点餐系统` ```java public class OrderPizza { private Pizza pizza = null; /** * 点餐 */ public OrderPizza() { String orderType = ""; do { orderType = getType(); pizza = PizzaSimpleFactory.createPizza(orderType); if (this.pizza != null) { // 输出制作 this.pizza.prepare(); this.pizza.bake(); this.pizza.cut(); this.pizza.box(); } else { System.out.println("取消订购"); break; } } while (true); } /** * 工具类选择披萨类型 * * @return */ private String getType() { try { BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("========披萨订购系统========="); System.out.println("1:希腊披萨 2:奶酪披萨 其他:退出"); System.out.print("请选择:"); String str = strin.readLine(); return str; } catch (IOException e) { e.printStackTrace(); return ""; } } } ``` `简单工厂类` ```java public class PizzaSimpleFactory { /** * 通过用户选择返回不同类型的披萨 * @param orderType 1:希腊披萨 2:奶酪披萨 其他:null * @return */ public static Pizza createPizza(String orderType) { Pizza pizza = null; switch (orderType) { case "1" : { pizza = new GreekPizza(); break; } case "2" : { pizza = new CheesePizza(); break; } } return pizza; } } ``` ` 披萨店` ```java public class PizzaStore { public static void main(String[] args) { new OrderPizza(); } } ``` ## 工厂方法模式 **模式介绍:** 工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。 工厂方法模式是[简单工厂模式](https://baike.baidu.com/item/简单工厂模式)的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。 [2] 工厂方法模式对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不再负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。 工厂方法模式(Factory Method pattern)是最典型的模板方法模式(Template Method pattern)应用。 **案例:** 在上述基础上,披萨店面扩大,现有有伦敦披萨店和北京披萨店,每个店有各自的具有当地特色的披萨产品(不一定名称相同), ![factory_method](factory/src/com/xy/factorymethod/factory_method.png) `披萨抽象类:` ```java public abstract class Pizza { protected String name; public abstract void prepare(); public void cut() { System.out.println(name + ", 开始切割"); } public void box() { System.out.println(name + ", 开始装盒"); } public void bake() { System.out.println(name + ", 开始烘培"); } public void setName(String name) { this.name = name; } } ``` `披萨子类:` ```java public class BJCheesePizza extends Pizza { @Override public void prepare() { System.out.println("制作北京奶酪披萨,准备原材料 "); setName("北京奶酪披萨"); } } ``` ```java public class BJGreekPizza extends Pizza{ @Override public void prepare() { System.out.println("制作北京的希腊披萨,准备原材料"); setName("北京的希腊披萨"); } } ``` ```java public class LDCheesePizza extends Pizza { @Override public void prepare() { System.out.println("制作伦敦奶酪披萨,准备原材料 "); setName("伦敦奶酪披萨"); } } ``` ```java public class LDGreekPizza extends Pizza{ @Override public void prepare() { System.out.println("制作伦敦的希腊披萨,准备原材料"); setName("伦敦的希腊披萨"); } } ``` `工厂抽象类:` ```java public abstract class OrderPizzaFactoryMethod { private Pizza pizza = null; // 定义一个抽象方法 abstract Pizza createPizza(String orderType); public OrderPizzaFactoryMethod() { String orderType = ""; do { orderType = getType(); pizza = createPizza(orderType); if (this.pizza != null) { // 输出制作 this.pizza.prepare(); this.pizza.bake(); this.pizza.cut(); this.pizza.box(); } else { System.out.println("取消订购"); break; } }while (true); } /** * 工具类选择披萨类型 * * @return */ private String getType() { try { BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("========披萨订购系统========="); System.out.println("1:希腊披萨 2:奶酪披萨 其他:退出"); System.out.print("请选择:"); String str = strin.readLine(); return str; } catch (IOException e) { e.printStackTrace(); return ""; } } } ``` `工厂子类:` ```java public class BJOrderPizzaFactory extends OrderPizzaFactoryMethod { @Override Pizza createPizza(String orderType) { Pizza pizza = null; switch (orderType) { case "1" : { pizza = new BJGreekPizza(); break; } case "2" : { pizza = new BJCheesePizza(); break; } } return pizza; } } ``` ```java public class LDOrderPizzaFactory extends OrderPizzaFactoryMethod { @Override Pizza createPizza(String orderType) { Pizza pizza = null; switch (orderType) { case "1" : { pizza = new LDGreekPizza(); break; } case "2" : { pizza = new LDCheesePizza(); break; } } return pizza; } } ``` `点餐系统` ```java public class PizzaStore { public static void main(String[] args) throws IOException { order(); } private static void order() throws IOException{ BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("=========请选择地区========="); System.out.println("1:北京 2:伦敦 其他:退出"); System.out.print("请选择:"); String city = strin.readLine(); switch (city) { case "1" : { new BJOrderPizzaFactory(); break; } case "2" : { new LDOrderPizzaFactory(); break; } } } } ``` ## 抽象工厂模式 **模式介绍:** 抽象工厂模式相对于[工厂方法模式](https://baike.baidu.com/item/工厂方法模式/2361103)来说,就是工厂方法模式是针对一个产品系列的,而抽象工厂模式是针对多个产品系列的,即工厂方法模式是一个产品系列一个[工厂类](https://baike.baidu.com/item/工厂类/10304671),而抽象工厂模式是多个产品系列一个工厂类。在抽象工厂模式中,[客户端](https://baike.baidu.com/item/客户端/101081)不再负责对象的创建,而是把这个责任丢给了具体的工厂类,客户端只负责对对象的调用,从而明确了各个类的职责。并且当一系列相互关联的产品被设计到一个工厂类里后,客户端的调用将会变得非常简单,而且,如果要更换这一系列的产品,则只需要更换一个工厂类即可。 ![image-20220611094123063](factory/src/com/xy/absfactory/image.png) 详情 --> [ **五分钟学设计模式.04.抽象工厂模式**](https://www.bilibili.com/video/BV1y54y1B7k6?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=07a5272307392297d8b6063237e0e704) # 设计模式——原型模式 ## **基本介绍** - 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。 - 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。 - 工作原理是:通过将--个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它 们自己来实施创建,即对象.clone()。 - 形象的理解:孙大圣拔出猴毛,变出其它孙大圣 ## UML结构 ![Prototype](prototype/src/com/xy/Prototype.png) **原理结构图说明:** - Prototype:原型类,声明一个克隆自己的接口 - ConcretePrototype:具体的原型类,实现一个克隆自己的操作 - Client:让一个原型对象克隆自己,从而创建一个新的对象(属性一样) ## 原型模式——浅拷贝 案例:通过代码实现克隆羊 ![clone](prototype/src/com/xy/improve/clone.png) **代码实现:** 步骤: - 实现Cloneable接口 - 重写clone方法 ```java public class Sheep implements Cloneable{ private String name; private int age; private String color; public Sheep friend; public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } /** * 克隆该实例,使用默认方法来完成 * @return */ @Override protected Sheep clone() { Sheep sheep = null; try { sheep = (Sheep) super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.getMessage()); } return sheep; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + '}'; } } ``` `客户端代码` ```java public class Client { public static void main(String[] args) { Sheep sheep1 = new Sheep("tom", 1, "白色"); sheep1.friend = new Sheep("jack", 2, "黑色"); Sheep sheep2 = sheep1.clone(); System.out.println("克隆羊是否为同一只:" + (sheep2 == sheep1)); // 对象为浅拷贝 System.out.println("克隆羊的朋友是否为同一只:" + (sheep2.friend == sheep1.friend)); } } ``` **引发问题:** - 对于**数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,**也就是将该属性值复制一份给新的对象。 - 对于数**据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递**,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都**指向同一个实例**。在这种情况下,**在一个对象中修改该成员变量会影响到另-一个对象的该成员变量值**。 ## 原型模式——深拷贝 **基本介绍** - 复制对象的所有基本数据类型的成员变量值。 - 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。 **实现方式** - 深拷贝实现方式1:**重写clone方法来实现深拷贝。** - 深拷贝实现方式2:**通过对象序列化实现深拷贝(推荐)** ### 深拷贝方式一:重写clone `实现代码如下:` ```java public class Sheep implements Cloneable { private String name; private int age; private String color; public Sheep friend; public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } /** * 克隆深拷贝 * * @return */ @Override protected Sheep clone() { Sheep sheep = null; try { sheep = (Sheep) super.clone(); Sheep friend = sheep.friend; if (friend != null) { sheep.friend = sheep.friend.clone(); } } catch (CloneNotSupportedException e) { System.out.println(e.getMessage()); } return sheep; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + '}'; } } ``` `客户端代码` ```java public class Client { public static void main(String[] args) { Sheep sheep1 = new Sheep("tom", 1, "白色"); sheep1.friend = new Sheep("jack", 2, "黑色"); Sheep sheep2 = sheep1.clone(); System.out.println("克隆羊是否为同一只:" + (sheep2 == sheep1)); // 对象为浅拷贝 System.out.println("克隆羊的朋友是否为同一只:" + (sheep2.friend == sheep1.friend)); } } ``` **引发问题:** 如果引用十分复杂,或则应用对象十分多,会导致逻辑复杂,代码冗余等问题(**不推荐使用**),`推荐使用序列化方式` ### 深拷贝方式二:对象序列化 `实现代码如下:` ```java public class Sheep implements Cloneable, Serializable { private static final long serialVersionUID = 1L; private String name; private int age; private String color; public Sheep friend; public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } /** * 克隆深拷贝 序列化 * * @return */ @Override protected Sheep clone() { Sheep sheep = null; try( ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos) ){ oos.writeObject(this); try ( ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); ){ sheep = (Sheep) ois.readObject(); } }catch (Exception e) { System.out.println(e.getMessage()); } return sheep; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + '}'; } } ``` `客户端代码` ```java public class Client { public static void main(String[] args) { Sheep sheep1 = new Sheep("tom", 1, "白色"); sheep1.friend = new Sheep("jack", 2, "黑色"); Sheep sheep2 = sheep1.clone(); System.out.println("克隆羊是否为同一只:" + (sheep2 == sheep1)); // 对象为浅拷贝 System.out.println("克隆羊的朋友是否为同一只:" + (sheep2.friend == sheep1.friend)); } } ``` ## 原型模式的注意事项和细节 - 创建新的对象比较复杂时,可以利用原型模式**简化对象的创建过程,同时也能够提高效率**。 - 不用重新初始化对象,而是**动态地获得对象运行时的状态**。 - 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码。 - 在实现深克隆的时候可能需要比较复杂的代码。 - 缺点:需要为每一个类配备一个克隆方法,**这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则**。 # 设计模式——建造者模式 ## 基本介绍 - 建造者模式(Builder Pattern)又叫`生成器模式`,是一种对象**构建模式**。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 - 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。 ## UML结构 ![improve](builder/src/com/xy/improve/improve.png) 角色解析: - Product(产品角色):一个具体的产品对象。 - Builder (抽象建造者) :创建一个Product对象的各个部件指定的接口/抽象类。 - ConcreteBuilder (具体建造者) :实现接口, 构建和装配各个部件。 - Director (指挥者) :构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。 ## 建造者案例 背景介绍: 将工程中的建造房屋的需求抽象成bulidBasic(打地基)、buildWall(砌墙)、roofed(封顶)、build(交付产品),无论是高楼大厦还是普通民房都需要通过该步骤完成,通过建造者模式可以方便的完成该需求 ![improve2](builder/src/com/xy/improve/improve2.png) `House(产品)` ```java public class House { private String basic; private String wall; private String roofed; @Override public String toString() { return "House{" + "basic='" + basic + '\'' + ", wall='" + wall + '\'' + ", roofed='" + roofed + '\'' + '}'; } public String getBasic() { return basic; } public void setBasic(String basic) { this.basic = basic; } public String getWall() { return wall; } public void setWall(String wall) { this.wall = wall; } public String getRoofed() { return roofed; } public void setRoofed(String roofed) { this.roofed = roofed; } } ``` `HouseBuilder(抽象建造者)` ```java public abstract class HouseBuilder { protected House house = new House(); // 建造房子 abstract void buildBasic(); abstract void buildWall(); abstract void buildRoofed(); /** * * @return 返回建造好的房屋 */ public House getBuildHouse() { return house; } } ``` `CommonBuilder、HighBuilding(具体建造者)` ``` public class CommonBuilder extends HouseBuilder{ @Override void buildBasic() { house.setBasic("普通房子地基"); } @Override void buildWall() { house.setWall("普通房子砌墙"); } @Override void buildRoofed() { house.setRoofed("普通房子封顶"); } } ``` ``` public class HighBuilding extends HouseBuilder{ @Override void buildBasic() { house.setBasic("高楼房子地基"); } @Override void buildWall() { house.setWall("高楼房子砌墙"); } @Override void buildRoofed() { house.setRoofed("高楼房子封顶"); } } ``` `HouseDirector(指挥者)` ```java public class HouseDirector { private HouseBuilder houseBuilder = null; /** * 构造器传入 * @param houseBuilder */ public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } public void setHouseBuilder(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } /** * 建造房屋过程 * @return */ public House constructHouse() { houseBuilder.buildBasic(); houseBuilder.buildWall(); houseBuilder.buildRoofed(); return houseBuilder.house; } } ``` `客户端` ```java public class Client { public static void main(String[] args) { HouseDirector houseDirector = new HouseDirector(new CommonBuilder()); House house = houseDirector.constructHouse(); System.out.println("house = " + house); houseDirector.setHouseBuilder(new HighBuilding()); House house1 = houseDirector.constructHouse(); System.out.println("house1 = " + house1); } } ``` ## 建造者模式的注意事项和细节 - 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。 - 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。 - 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。 - 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“**开闭原则**”。 - 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,**如果产品之间的差异性很大,则不适合使用建造者模式**,因此其使用范围受到一定的限制。 - 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式。 - `抽象工厂模式VS建造者模式` - 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生-一个新产品。 # 设计模式——适配器模式 ## 基本介绍 - 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper) - 适配器模式属于结构型模式 - 主要分为三类:**类适配器模式、对象适配器模式、接口适配器模式** ## 工作原理 - 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容。 - 从用户 的角度看不到被适配者,是解耦的。 - 用户 调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法。 - 用户收到反馈结果, 感觉只是和目标接口交互。 ## 类适配器模式 背景介绍:在中国电源大都是220伏的,而日常生活中手机的充电确是5伏1安(苹果),通过类适配器模拟电源适配器实现手机充电功能 ![class_adapter](adapter/src/com/xy/classadapter/class_adapter.png) `Voltage220V(被适配电源)` ```java public class Voltage220V { public int output220V() { int src = 220; System.out.println("电压 = " + src + "伏"); return src; } } ``` `Voltage5V(目标输出)` ```java public interface Voltage5V { int output5V(); } ``` `VoltageAdapter(电源适配器)` ```java public class VoltageAdapter extends Voltage220V implements Voltage5V { @Override public int output5V() { int src = output220V(); int dst = src / 44; System.out.printf("适配器将%d伏====>%d伏\n", src, dst); return dst; } } ``` `Phone(手机充电)` ```java public class Phone { public void charging(Voltage5V v) { int src = v.output5V(); if (src <= 5 && src > 0) { System.out.println("电压 = " + src + "伏,可以充电..."); } else if (src > 5) { System.out.println("电压过高"); } else { System.out.println("没有充电..."); } } } ``` `Client` ```java public class Client { public static void main(String[] args) { System.out.println("====类适配器模式===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter()); } } ``` **类适配器模式注意事项和细节** - Java是单继承机制,所以类适配器需要继承src类这一点算是- -一个缺点,因为这要求dst必须是接口,有一定局限性; - src类的方法在Adapter中都会暴露出来,也增加了使用的成本。 ## 对象适配器模式 **优点:** - 基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。即: 持有sre类,实现dst类接口,完成src->dst的适配 - 根据“合成复用原则”,在系统中**尽量使用关联关系(聚合)来替代继承关系。** - 对象适配器模式是适配器模式常用的一种 **UML类图:** ![object_adapter](adapter/src/com/xy/objectadapter/object_adapter.png) 其他代码与上一致,修改电源适配器即可: ```java public class VoltageAdapter implements Voltage5V { private Voltage220V voltage220V = null; public VoltageAdapter(Voltage220V voltage220V) { this.voltage220V = voltage220V; } @Override public int output5V() { int src = 0; if (voltage220V != null) { src = voltage220V.output220V(); } int dst = src / 44; System.out.printf("适配器将%d伏====>%d伏\n",src,dst); return dst; } } ``` `Client(测试)` ```java public class Client { public static void main(String[] args) { System.out.println("====对象适配器模式===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter(new Voltage220V())); System.out.println("--------------------"); phone.charging(new VoltageAdapter(null)); } } ``` ## 接口适配器模式 简介: - 一些书籍称为:适配器模式(Default Adapter Pattermn)或缺省适配器模式。 - 核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。 - 适用于一个接口不想使用其所有的方法的情况。 UML类图 背景介绍:模拟springMVC源码展示接口设计模式 ![Adapter](adapter/src/com/xy/interfaceadapter/Adapter.png) `接口适配器和实现类` ```java ///定义一个Adapter接口 public interface HandlerAdapter { public boolean support(Controller handler); public void handle(Controller handler); } // 多种适配器类 class SimpleHandlerAdapter implements HandlerAdapter { public void handle(Controller handler) { handler.doHandler(); } public boolean support(Controller handler) { return (handler instanceof SimpleController); } } class HttpHandlerAdapter implements HandlerAdapter { public void handle(Controller handler) { handler.doHandler(); } public boolean support(Controller handler) { return (handler instanceof HttpController); } } class AnnotationHandlerAdapter implements HandlerAdapter { public void handle(Controller handler) { handler.doHandler(); } public boolean support(Controller handler) { return (handler instanceof AnnotationController); } } ``` `模拟Controller` ```java //多种Controller实现 public interface Controller { void doHandler(); } class HttpController implements Controller { @Override public void doHandler() { System.out.println("http..."); } } class SimpleController implements Controller { @Override public void doHandler() { System.out.println("simple..."); } } class AnnotationController implements Controller { @Override public void doHandler() { System.out.println("annotation..."); } } ``` `模拟DispatchServlet` ```java public class DispatchServlet { public static List handlerAdapters = new ArrayList(); public DispatchServlet() { handlerAdapters.add(new AnnotationHandlerAdapter()); handlerAdapters.add(new HttpHandlerAdapter()); handlerAdapters.add(new SimpleHandlerAdapter()); } public void doDispatch(Controller controller) { // 得到对应适配器 HandlerAdapter adapter = getHandler(controller); // 通过适配器执行对应的controller对应方法 if (adapter != null) { adapter.handle(controller); } else { System.out.println("没有该适配器..."); } } public HandlerAdapter getHandler(Controller controller) { //遍历:根据得到的controller(handler), 返回对应适配器 for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.support(controller)) { return adapter; } } return null; } public static void main(String[] args) { new DispatchServlet().doDispatch(new SimpleController() ); new DispatchServlet().doDispatch(new HttpController() ); new DispatchServlet().doDispatch(new AnnotationController()); } } ``` ## 适配器模式的注意事项和细节 - 三种命名方式,是根据sre 是以怎样的形式给到Adapter (在Adapter里的形式)来命名的。 - **类适配器**:以类给到,在Adapter里,就是将src当做类,继承 - **对象适配器**:以对象给到,在Adapter里,将sre作为一个对象,持有 - **接口适配器**:以接口给到,在Adapter里,将src作为一个接口,实现Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。 # 设计模式——桥接模式 ## 模式介绍 - 桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。 - 是一种结构型设计模式。 - Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。 ## UML类图 ![Bridge](bridge/src/com/xy/Bridge.png) 类图说明: - Client类:桥接模式的调用者。 - 抽象类(Abstraction) :维护了Implementor /即它的实现类Concretelmplementor..二者是聚合关系,Abstraction充当桥接类 - RefinedAbstraction:是Abstraction 抽象类的子类 - Implementor: 行为实现类的接口 - ConcretelmplementorA/B :行为的具体实现类 - 从UML图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系 ## 桥接模式案例 背景介绍: 客户端通过桥接模式,根据手机功能和品牌进行相应功能的输出 ![PhoneBridge](bridge/src/com/xy/PhoneBridge.png) `手机品牌接口类及实现子类` ```java public interface Brand { void open(); void close(); void call(); } ``` ```java public class XiaoMi implements Brand { @Override public void open() { System.out.println("小米手机开机"); } @Override public void close() { System.out.println("小米手机关机"); } @Override public void call() { System.out.println("小米手机打电话"); } } ``` ```java public class Vivo implements Brand{ @Override public void open() { System.out.println("Vivo手机开机"); } @Override public void close() { System.out.println("Vivo手机关机"); } @Override public void call() { System.out.println("Vivo手机打电话"); } } ``` `手机抽象类及实现子类` ```java public abstract class Phone { private Brand brand; public Phone(Brand brand) { this.brand = brand; } protected void open(){ this.brand.open(); } protected void close(){ this.brand.close(); } protected void call() { this.brand.call(); } } ``` ```java public class FoldedPhone extends Phone{ public FoldedPhone(Brand brand) { super(brand); } @Override protected void open() { super.open(); System.out.println("折叠样式手机"); } @Override protected void close() { super.close(); System.out.println("折叠样式手机"); } @Override protected void call() { super.call(); System.out.println("折叠样式手机"); } } ``` ```java public class UpRightPhone extends Phone { public UpRightPhone(Brand brand) { super(brand); } @Override protected void open() { super.open(); System.out.println("直立样式手机"); } @Override protected void close() { super.close(); System.out.println("直立样式手机"); } @Override protected void call() { super.call(); System.out.println("直立样式手机"); } } ``` `客户端` ```java public class Client { public static void main(String[] args) { Phone foldedPhone = new FoldedPhone(new XiaoMi()); Phone upRightPhone = new UpRightPhone(new Vivo()); foldedPhone.close(); foldedPhone.open(); foldedPhone.call(); System.out.println("==============="); upRightPhone.close(); upRightPhone.open(); upRightPhone.call(); } } ``` ## 桥接模式的注意事项和细节 - 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。 - 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。 - 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。 - 桥接 模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设。 - 桥接 模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有-定的局限性,即需要有这样的应用场景。 - 桥接模式其它应用场景 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用. ## 常见的应用场景: - JDBC驱动程序 - 银行转账系统 转账分类:网上转账,柜台转账,AMT转账 转账用户类型:普通用户,银卡用户,金卡用户.. - 消息管理 消息类型:即时消息,延时消息 消息分类:手机短信,邮件消息,QQ消息... # 设计模式——装饰者模式 ## 模式介绍 23种设计模式之一,英文叫Decorator Pattern,又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 ## UML类图 ![decorator](decorate/src/com/xy/decorator.png) 类图解析: - Componet:主体 - ConcreateComponent:主体具体实现类 - Decorator:装饰者 - ConcreteDecorate:装饰者具体实现类 ## 装饰者模式案例 背景介绍:有一家咖啡店,店中有许多品种的咖啡(ShortBlack、Decaf、Espresso...),咖啡可以添加不同口味(Chocolate、Milk、Soy等),采用装饰者模式可以很好的解决该问题 ![coffee](decorate/src/com/xy/drink/coffee.png) 装饰者下单 (2份巧克力+1份牛奶+LangBlack) 如下图所示: ![example](decorate/src/com/xy/drink/example.png) `Drink、Coffee及其子类代码如下:` ```java public abstract class Drink { private String des;//描述 private float price = 0.0f; /** * 计算价格的方法 * @return */ public abstract float cost(); public String getDes() { return des; } public void setDes(String des) { this.des = des; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } } ``` ```java public class Coffee extends Drink{ @Override public float cost() { return super.getPrice(); } } ``` ```java public class ShortBlack extends Coffee{ public ShortBlack() { setDes("浓缩咖啡"); setPrice(4); } } ``` ```java public class Espresso extends Coffee{ public Espresso() { setPrice(6); setDes("意大利咖啡"); } } ``` ```java public class LongBlack extends Coffee{ public LongBlack() { setPrice(5); setDes("美式咖啡"); } } ``` `Decorator及其子类` ```Java public class Decorator extends Drink { private Drink drink; public Decorator(Drink drink) { // 聚合 this.drink = drink; } @Override public float cost() { // 调料的价格 + 咖啡的价格 return super.getPrice() + drink.cost(); } @Override public String getDes() { return super.getDes() + " && " + drink.getDes(); } } ``` ```java public class Chocolate extends Decorator{ public Chocolate(Drink drink) { super(drink); setDes("巧克力"); setPrice(3); } } ``` ```java public class Milk extends Decorator{ public Milk(Drink drink) { super(drink); setDes("牛奶"); setPrice(2); } } ``` ```java public class Soy extends Decorator{ public Soy(Drink drink) { super(drink); setDes("豆浆"); setPrice(1.5f); } } ``` `CoffeeBar测试类` ```java public class CoffeeBar { public static void main(String[] args) { // 点一份咖啡 Drink order = new LongBlack(); System.out.println(order.cost() + "$\t" + order.getDes()); // 加一份牛奶 order = new Milk(order); System.out.println(order.cost() + "$\t" + order.getDes()); // 加两份巧克力 order = new Chocolate(order); System.out.println(order.cost() + "$\t" + order.getDes()); order = new Chocolate(order); System.out.println(order.cost() + "$\t" + order.getDes()); } } ``` ## 装饰者模式优点 - Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。 - 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。 ## 装饰者模式缺点 - 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。 - 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。 - 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。 # 设计模式——组合模式 ## 模式介绍 - 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体_部分”的层次关系。 - 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。 - 这种类型的设计模式属于结构型模式。 - 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一-致的方式处理个别对象以及组合对象 ## UML类图 ![composite](composite/src/com/xy/composite.png) **类图解析:** - Component :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理。Component子部件, Component可以是抽象类或者接口。 - Leaf:在组合中表示叶子节点,叶子节点没有子节点 - Composite :非叶子节点,用于存储子 部件,在 Component接口中实现子部件的相关操作,比如增加(add),删除(delete)。 ## 组合模式案例 背景介绍:一所大学包含多个学院,学院又包含多个学科专业 ![composite2](composite/src/com/xy/composite2.png) `OrganizationComponent(抽象类)` ```java public abstract class OrganizationComponent { private String name; private String des; /** * 默认实现 * @param o */ protected void add(OrganizationComponent o) { throw new UnsupportedOperationException("该方法不支持"); } protected void remove(OrganizationComponent o) { throw new UnsupportedOperationException("该方法不支持"); } public OrganizationComponent(String name, String des) { this.name = name; this.des = des; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } /** * 打印方法 */ protected abstract void print(); } ``` `Department(专业类)` ``` public class Department extends OrganizationComponent{ public Department(String name, String des) { super(name, des); } @Override protected void print() { System.out.println(getName()); } } ``` `College(学院类)` ```java public class College extends OrganizationComponent { List departments = new ArrayList<>(); public College(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent o) { departments.add(o); } @Override protected void remove(OrganizationComponent o) { departments.remove(o); } @Override protected void print() { System.out.println("-----------" + getName() + "-----------"); departments.forEach(OrganizationComponent::print); } } ``` `University(大学类)` ```java public class University extends OrganizationComponent { List colleges = new ArrayList<>(); public University(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent o) { colleges.add(o); } @Override protected void remove(OrganizationComponent o) { colleges.remove(o); } @Override protected void print() { System.out.println("-----------" + getName() + "-----------"); colleges.forEach(OrganizationComponent::print); } } ``` `Client(测试类)` ```java public class Client { public static void main(String[] args) { // 创建大学 University university = new University("清华大学", "中国顶级大学"); // 计算机学院 College computerCollege = new College("计算机学院", "计算机学院"); College infoEngineerCollege = new College("信息工程学院", "信息工程学院"); // 创建专业 并添加 computerCollege.add(new Department("软件工程","软件工程不错")); computerCollege.add(new Department("网络工程","网络工程不错")); computerCollege.add(new Department("计算机科学与技术","计算机科学与技术老牌专业")); infoEngineerCollege.add(new Department("通信工程","通信工程不好学")); infoEngineerCollege.add(new Department("信息工程","信息工程好学")); // 添加学院到大学 university.add(computerCollege); university.add(infoEngineerCollege); // 打印 university.print(); } } ``` ## 组合模式的注意事项和细节 - 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。 - 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动。 - 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构。 - 需要**遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式**。 - 要求较高的抽象性,如果**节点和叶子有很多差异性**的话,比如很多方法和属性都不一样,不适合使用组合模式。 # 设计模式——外观模式 ## 模式介绍 - 外观模式(Facade) ,也叫“过程模式:外观模式为子系统中的一组接口**提供一个一致的界面**,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 - 外观模式通过定义一个一致的接口,用以**屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用**,而无需关心这个子系统的内部细节。 ## UML类图 ![facade](facade/src/com/xy/facade.png) **类图解析:** - **Facade**:为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象。 - **Client**:外观接口的调用者。 - **SubSystem集合**:指模块或者子系统,处理Facade对象指派的任务,他是功能的实际提供者。 ## 外观模式案例: **背景介绍:** 组建一个家庭影院:DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,通过直接用遥控器(统筹各设备开关)进行控制,其过程为: - 开爆米花机 - 放下屏幕 - 开投影仪 - 开音响 - 开DVD,选dvd - 拿爆米花 - 调暗灯光 - 播放 - 观影结束后,关闭各种设备. ![facade2](facade/src/com/xy/facade2.png) `DVD、Popcorn、Projector、Screen、Stereo、TheaterLight代码如下:` ```java public class DVDPlayer { // 使用单例模式 private static final DVDPlayer instance = new DVDPlayer(); private DVDPlayer() {} public static DVDPlayer getInstance() { return instance; } public void on() { System.out.println("dvd 打开了..."); } public void off() { System.out.println("dvd 关闭了..."); } public void play() { System.out.println("dvd 播放中..."); } public void pause() { System.out.println("dvd 暂停了..."); } } ``` ```java public class Popcorn { private static final Popcorn instance = new Popcorn(); private Popcorn(){} public static Popcorn getInstance() { return instance; } public void on() { System.out.println("爆米花机打开了..."); } public void off() { System.out.println("爆米花机关闭了..."); } public void pop() { System.out.println("爆米花做好了..."); } } ``` ```java public class Projector { private static final Projector instance = new Projector(); private Projector(){} public static Projector getInstance() { return instance; } public void on() { System.out.println("投影仪打开了..."); } public void off() { System.out.println("投影仪关闭了..."); } public void focus() { System.out.println("投影仪聚焦中..."); } } ``` ```java public class Screen { private static final Screen instance = new Screen(); private Screen(){} public static Screen getInstance() { return instance; } public void up() { System.out.println("屏幕上升..."); } public void down() { System.out.println("屏幕下降..."); } } ``` ```java public class Stereo { private static final Stereo instance = new Stereo(); private Stereo(){} public static Stereo getInstance() { return instance; } public void on() { System.out.println("立体音响打开..."); } public void off() { System.out.println("立体音响关闭..."); } public void up() { System.out.println("立体音响音量+..."); } public void down() { System.out.println("立体音响音量-..."); } } ``` ```java public class TheaterLight { private static final TheaterLight instance = new TheaterLight(); private TheaterLight(){} public static TheaterLight getInstance() { return instance; } public void on() { System.out.println("灯光打开..."); } public void off() { System.out.println("灯光关闭..."); } public void dim() { System.out.println("灯光亮度调暗..."); } public void bright() { System.out.println("灯光亮度调亮..."); } } ``` `HomeTheaterFacade(统筹各设备开关)` ```java public class HomeTheaterFacade { private DVDPlayer dvdPlayer; private Popcorn popcorn; private Projector projector; private Stereo stereo; private Screen screen; private TheaterLight theaterLight; public HomeTheaterFacade() { this.dvdPlayer = DVDPlayer.getInstance(); this.popcorn = Popcorn.getInstance(); this.projector = Projector.getInstance(); this.stereo = Stereo.getInstance(); this.screen = Screen.getInstance(); this.theaterLight = TheaterLight.getInstance(); } /** * 准备开始 */ public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); projector.focus(); stereo.on(); dvdPlayer.on(); theaterLight.dim(); } /** * 播放 */ public void play() { dvdPlayer.play(); } /** * 暂停 */ public void pause() { dvdPlayer.pause(); } /** * 结束 */ public void end() { popcorn.off(); theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dvdPlayer.off(); } } ``` `Client(测试)` ```java public class Client { public static void main(String[] args) { HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(); System.out.println("----------准备开始-----------"); homeTheaterFacade.ready(); System.out.println("----------开始播放-----------"); homeTheaterFacade.play(); System.out.println("----------播放暂停-----------"); homeTheaterFacade.pause(); System.out.println("----------播放结束-----------"); homeTheaterFacade.end(); } } ``` ## 外观模式的注意事项和细节 - 外观模式**对外屏蔽了子系统的细节**,因此外观模式降低了客户端对子系统使用的复杂性。 - 外观模式对客户端与子系统的耦合关系-解耦,让子系统内部的模块更易维护和扩展 - 通过合理的使用外观模式,可以帮我们更好的**划分访问的层次**。 - 当系统需要进行分层设计时,可以考虑使用Facade模式。 - 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。 - 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。 # 设计模式——享元模式 ## 模式介绍 - 享元模式( **Flyweight Pattern**)也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象。 - 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则**直接拿来用,避免重新创建**,如果没有我们需要的,则创建一个。 - **享元模式能够解决重复对象的内存浪费的问题**,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率 - 享元模式经典的应用场景就是池技术了,**String常量池、数据库连接池、缓冲池**等等都是享元模式的应用,享元模式是池技术的重要实现方式 ## UML类图 ![flyweight](flyweight/src/com/xy/flyweight.png) **类图解析:** - **FlyWeight**是抽象的享元角色,他是产品的**抽象类**,同时定义出**对象的外部状态和内部状态**接口或实现。 - **ConcreteFlyWeight**是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务。 - **UnSharedConcreteFlyWeight**是不可共享的角色,一般不会出现在享元工厂。 - **FlyWeightFactory**享元工厂 类,用于构建一个**池容器**(集合),同时 提供从池中获取对象方法。 - 享元模式提出了两个要求:细粒度和共享对象,将对象的信息分为两个部分: - **内部状态**:指对象共享出来的信息,**存储在享元对象内部且不会随环境的改变而改变**。 - **外部状态**:指对象得以依赖的一个标记,**是随环境改变而改变的、不可共享的状态**。 ## 享元模式案例 背景介绍:模拟共享单车租赁系统,用户提供姓名信息可以租赁单车、归还单车等操作 ![flyweight3](flyweight/src/com/xy/example2/flyweight3.png) **代码实现:** `User类(用户信息类)` ```java public class User { String name; public User(String name) { this.name = name; } public String getName() { return name; } } ``` `BikeFlyweight(抽象类)` ```java public abstract class BikeFlyweight { // 内部状态 0 未使用 1 使用中 protected Integer state = 0; /** * @param user 外部状态 */ abstract void ride(User user); /** * 归还自行车 */ abstract void back(); public Integer getState() { return state; } } ``` `MoBikeFlyweight(具体实现类)` ```java public class MoBikeFlyweight extends BikeFlyweight { // 定义新的累不状态,车牌号 private String bikeId; public MoBikeFlyweight(String bikeId) { this.bikeId = bikeId; } @Override void ride(User user) { state = 1; System.out.println(user.getName() + "骑" + bikeId + "自行车出行"); } @Override void back() { state = 0; } } ``` `BikeFlyweightFactory(享元工厂类)` ```java public class BikeFlyweightFactory { // 单例模式 饿汉式 private static final BikeFlyweightFactory instance = new BikeFlyweightFactory(); // 创建自行车池 private Set pool = new HashSet<>(); // 放入单车 private BikeFlyweightFactory() { for (int i = 0; i < 2; i++) { pool.add(new MoBikeFlyweight(i + 1 + "号")); } } public static BikeFlyweightFactory getInstance() { return instance; } public BikeFlyweight getBike() { for (BikeFlyweight bike : pool) { if (bike.getState() == 0) { return bike; } } return null; } } ``` `Client(测试类)` ```java public class Client { public static void main(String[] args) { // 张三骑车使用中,未归还 BikeFlyweight bike1 = BikeFlyweightFactory.getInstance().getBike(); bike1.ride(new User("张三")); // bike1.back(); // 李四租赁单车并归还 BikeFlyweight bike2 = BikeFlyweightFactory.getInstance().getBike(); bike2.ride(new User("李四")); bike2.back(); // 王五租赁单车 BikeFlyweight bike3 = BikeFlyweightFactory.getInstance().getBike(); bike3.ride(new User("王五")); bike3.back(); System.out.println("张三骑的车 == 李四骑的车" + (bike1 == bike2)); System.out.println("李四骑的车 == 王五骑的车" + (bike3 == bike2)); } } ``` ## 享元模式的注意事项和细节 - 在享元模式这样理解,“ 享”就表示共享,“元” 表示对象。 - 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,可以考虑选用享元模式。 - 用唯一标识码判断,如果在内存中有,则返回这个唯一标 识码所标识的对象,用HashMap/HashTable存储。 - 享元模式**大大减少了对象的创建,降低了程序内存的占用,提高效率**。 - 享元模式**提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方**。 - 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。 - 享元模式经典的应用场景是需要缓冲池的场景,比如**String常量池、数据库连接池**。 # 设计模式——代理模式 ## 模式介绍 - 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 - 当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。 - 代理模式有不同的形式,主要有三种**静态代理、动态代理(JDK 代理、接口代理)和Cglib 代理(可以在内存动态的创建对象,而不需要实现接口,他是 属于动态代理的范畴)**。 ## UML类图 ![proxy](proxy/src/com/xy/proxy.png) **类图解析:** Subject:通过接口或抽象类声明真实角色实现的业务方法。 Proxy:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。 RealSubject:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。 ## 静态代理 背景介绍:老师生病了,需要另一名老师帮其代课 ![static](proxy/src/com/xy/static.png) `ITeacherDao接口类` ```java public interface ITeacherDao { /** * 老师上课 */ void teach(); } ``` `TeacherDao实现类` ``` public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("开始上课..."); } } ``` `TeacherDaoProxy代理类` ```java public class TeacherDaoProxy implements ITeacherDao{ // 被代理对象 private ITeacherDao iTeacherDao; public TeacherDaoProxy(ITeacherDao iTeacherDao) { this.iTeacherDao = iTeacherDao; } public TeacherDaoProxy() { try { iTeacherDao =(ITeacherDao) this.getClass().getClassLoader().loadClass("com.xy.staticproxy.TeacherDao").newInstance(); } catch (Exception e) { System.err.println(e.getMessage()); } } /** * 上课前 */ public void preTeach() { System.out.println("你们老师生病了, 我来代一节课..."); } @Override public void teach() { preTeach(); iTeacherDao.teach(); afterTeach(); } /** * 上课后 */ private void afterTeach() { System.out.println("下课了, 同学们有缘再见..."); } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 生病的老师, 被代理对象 ITeacherDao patientTeacher = new TeacherDao(); // 新老师, 代理对象 //ITeacherDao proxyTeacher = new TeacherDaoProxy(patientTeacher); TeacherDaoProxy proxyTeacher = new TeacherDaoProxy(); // 老师代课 proxyTeacher.teach(); } } ``` **静态代理优缺点** - **优点**:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。 - **缺点**:因为**代理对象需要与目标对象实现一样的接口**,所以会有很多代理类一旦接口增加方法,目标对象与代理对象都要维护 ## 动态代理 **动态代理模式的基本介绍** - 代理对象,**不需要实现接口,但是目标对象要实现接口,否则不能用动态代理**。 - 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象动态代理也叫做: JDK代理、接口代理。 ![dynamic](proxy/src/com/xy/dynamic.png) `ITeacherDao接口类` ```java public interface ITeacherDao { /** * 老师上课 */ void teach(); } ``` `TeacherDao实现类` ```java public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("开始上课..."); } } ``` `ProxyFactory代理类` ```java public class ProxyFactory{ // 目标对象 private Object target; // 构造器,对target进行初始化 public ProxyFactory(Object target) { this.target = target; } /** * 方法执行前 */ public void preMethod() { System.out.println("方法执行前..."); } /** * 方法执行后 */ private void afterMethod() { System.out.println("方法执行后..."); } // 给目标对象生成代理对象 public Object getProxyInstance() { /** * ClassLoader loader, 指定类加载器 * Class[] interfaces,目标对象(被代理对象),使用泛型方式确认类型 * InvocationHandler h 事件处理执行目标对象的方法时,会触发时间处理器的方法 * 会把当前执行的目标对象方法作为参数传入 */ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { preMethod(); // 反射机制调用目标对象方法 Object invoke = method.invoke(target, args); afterMethod(); return invoke; }); } } ``` `Test用于测试动态代理效果` ```java public interface Test { void test(); } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 被代理对象 TeacherDao target = new TeacherDao(); // 创建代理对象 ITeacherDao proxyITeacher = (ITeacherDao)new ProxyFactory(target).getProxyInstance(); proxyITeacher.teach(); System.out.println("-------------"); Test test = (Test)new ProxyFactory((Test) () -> System.out.println("this is a test") ).getProxyInstance(); test.test(); } } ``` ## cglib代理 **Cglib代理模式的基本介绍** - 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候**目标对象只是一个单独的对象**,并**没有实现任何的接口**,这个时候可使用目标对象子类来实现代理,即**Cglib代理**。 - Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。 - Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,**例如Spring AOP,实现方法拦截**。 - 在AOP编程中如何选择代理模式: - **目标对象需要实现接口**,用JDK代理 - **目标对象不需要实现接口**,用Cglib代理 - Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类 ![cglib](proxy/src/com/xy/cglib.png) 导入cglib依赖jar包 ![image-20220616093447316](proxy/src/com/xy/img.png) `ProxyFactoryd代理工厂 ```java public class ProxyFactory implements MethodInterceptor { // 维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } /** * 返回一个target代理对象 * @return */ public Object getProxyInstance() { // 1、创建一个工具类 Enhancer enhancer = new Enhancer(); // 2、设置父类 enhancer.setSuperclass(target.getClass()); // 3、设置回调函数 enhancer.setCallback(this); // 4、返回子类,即代理对象 return enhancer.create(); } /** * 方法执行前 */ public void preMethod() { System.out.println("方法执行前..."); } /** * 方法执行后 */ private void afterMethod() { System.out.println("方法执行后..."); } // 重写拦截方法,调用目标方法 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { preMethod(); // 使用反射 //Object value = method.invoke(target, objects); // 使用fastClass Object value = methodProxy.invokeSuper(o, objects); afterMethod(); return value; } } ``` `TeacherDao和Test(被代理类)` ```java public class TeacherDao { public void teach() { System.out.println("老师授课中..."); } } class Test{ public void test() { System.out.println("this is a teat..."); } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 被代理类 TeacherDao target = new TeacherDao(); // 创建对象代理 TeacherDao proxyTeacher =(TeacherDao) new ProxyFactory(target).getProxyInstance(); // 执行方法 proxyTeacher.teach(); System.out.println("----------"); // 测试类 ((Test)new ProxyFactory(new Test()).getProxyInstance()).test(); } } ``` ## 几种常见的代理模式介绍——几种变体 - **防火墙代理**:内网通过代理穿透防火墙,实现对公网的访问。 - **缓存代理**:比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。 - **远程代理**:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。 - **同步代理**:主要使用在多线程编程中,完成多线程间同步工作。 # 设计模式——模板方法模式 ## 模式介绍 - 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern), 指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 - 简单说, 模板方法模式定义一个操作中的算法的骨架,而将- -些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。 - 这种类型的设计模式属于行为型模式。 ## UML类图 ![template](template/src/com/xy/template.png) **类图解析:** - AbstractClass抽象类,类中实现 了模板方法(template),定义了算法的骨架,具体子类需要去实现其它的抽象方法operationr2,3,4。 - ConcreteClass实现抽象方法operationr2,3,4,以完成算法中特点子类的步骤。 ## 模板方法模式案例 背景介绍:有一个豆浆店,根据豆浆的制作流程,大致可以分为选择原料,添加调料,浸泡和打碎四个步骤,豆浆的select、soak、beat方法基本不变所以可以在父类中直接实现,子类只需要实现add(添加调味如:花生、黑豆等)方法即可,父类中又最终方法make(制作豆浆的过程)规定了豆浆的制作过程。 ![bean](template/src/com/xy/bean.png) `SoyMilk抽象类` ```java public abstract class SoyMilk { protected String condiment; /** * 模板方法 */ public final void make() { select(); addCondiments(); soak(); beat(); } /** * 添加不同的配料 */ protected abstract void addCondiments(); /** * 选材料 */ private void select() { System.out.println("第一步:选择新鲜黄豆 "); } /** * 浸泡 */ private void soak() { System.out.println("第三步:黄豆和" + condiment + "开始浸泡,需要3小时"); } /** * 打碎 */ private void beat() { System.out.println("第四步:黄豆和" + condiment + "放到豆浆机中打碎"); } /** * 设置原料 * @param condiment */ protected void setCondiment(String condiment) { this.condiment = condiment; } } ``` `BlackBeanSoyMilk、PeanutSoyMilk具体实现子类` ```java public class BlackBeanSoyMilk extends SoyMilk{ @Override protected void addCondiments() { System.out.println("第二步:加入上好的黑豆"); setCondiment("黑豆"); } } ``` ```java public class PeanutSoyMilk extends SoyMilk{ @Override protected void addCondiments() { System.out.println("第二步:加入上好的花生"); setCondiment("花生"); } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 制作黑豆豆浆 System.out.println("-------制作黑豆豆浆-------"); SoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk(); blackBeanSoyMilk.make(); System.out.println("-------制作花生豆浆-------"); SoyMilk peanutSoyMilk = new PeanutSoyMilk(); peanutSoyMilk.make(); } } ``` ## 模板方法模式的钩子方法 - 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。 - 以做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,使用钩子方法对前面的模板方法进行改造。 只需**修改SoyMilk和添加PureSoyMilk**即可, 其他类不变化,代码如下所示: `SoyMilk抽象类` ```java public abstract class SoyMilk { protected String condiment = ""; /** * 模板方法 */ public final void make() { select(); if (customerWantCondiments()) { addCondiments(); } soak(); beat(); } /** * 添加不同的配料 */ protected abstract void addCondiments(); /** * 选材料 */ private void select() { System.out.println("第一步:选择新鲜黄豆 "); } /** * 浸泡 */ private void soak() { if(customerWantCondiments()) { System.out.println("第三步:黄豆和" + condiment + "开始浸泡,需要3小时"); } else { System.out.println("第二步:黄豆开始浸泡,需要3小时"); } } /** * 打碎 */ private void beat() { if(customerWantCondiments()) { System.out.println("第四步:黄豆和" + condiment + "放到豆浆机中打碎"); } else { System.out.println("第三步:黄豆放到豆浆机中打碎"); } } /** * 设置原料 * @param condiment */ protected void setCondiment(String condiment) { this.condiment = condiment; } /** * 钩子方法 */ boolean customerWantCondiments() { return true; } } ``` `PureSoyMilk类` ```java public class PureSoyMilk extends SoyMilk{ @Override protected void addCondiments() { } @Override boolean customerWantCondiments() { return false; } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 制作黑豆豆浆 System.out.println("-------制作黑豆豆浆-------"); SoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk(); blackBeanSoyMilk.make(); System.out.println("-------制作花生豆浆-------"); SoyMilk peanutSoyMilk = new PeanutSoyMilk(); peanutSoyMilk.make(); System.out.println("-------制作纯豆浆-------"); SoyMilk pureSoyMilk = new PureSoyMilk(); pureSoyMilk.make(); } } ``` ## 模板方法模式的注意事项和细节 - 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。 - 实现了**最大化代码复用**。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。 - 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。 - 该模式的**不足之处**:一个不同的实现都需要一个 子类实现,导致类的个数增加,使得系统更加庞大一般模板方法都加上final关键字,防止子类重写模板方法。 - 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。 # 设计模式——命令模式 ## 模式介绍 - 命令模式(Command Pattern) :在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计。 - 命名模式使得**请求发送者与请求接收者消除彼此之间的耦合**,让对象之间的调用关系更加灵活,实现解耦。 - 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。 ## UML类图 ![command](command/src/com/xy/command.png) **类图解析:** - **Invoker**:是调用者角色。 - **Command**:是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类 - **ConcreteCommand**:将-一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute、undo方法 - **Receiver**:接受者角色,知道如何实施和执行一个请求相关的操作 ## 命令模式案例 案例解析:智能家居,通过一个遥控器控制家里的智能设备 ![遥控器](command/src/com/xy/遥控器.png) ![command1](command/src/com/xy/command1.png) `Command接口类` ```java public interface Command { /** * 执行操作 */ void execute(); /** * 撤销操作 */ void undo(); } ``` `LightReceiver、CurtainReceiver信号接收者` ```java public class LightReceiver { public void on() { System.out.println("开灯..."); } public void off() { System.out.println("关灯...."); } } ``` ```java public class CurtainReceiver { public void on() { System.out.println("打开窗帘..."); } public void off() { System.out.println("关闭窗帘..."); } } ``` `Command具体实现子类CurtainOnCommand、CurtainOffCommand、LightOnCommand、LightOffCommand、NoCommand` ```java public class CurtainOnCommand implements Command { private CurtainReceiver curtain; public CurtainOnCommand(CurtainReceiver curtainReceiver) { this.curtain = curtainReceiver; } @Override public void execute() { curtain.on(); } @Override public void undo() { curtain.off(); } } ``` ```java public class CurtainOffCommand implements Command { private CurtainReceiver curtainReceiver; public CurtainOffCommand(CurtainReceiver curtainReceiver) { this.curtainReceiver = curtainReceiver; } @Override public void execute() { curtainReceiver.off(); } @Override public void undo() { curtainReceiver.on(); } } ``` ```java public class LightOnCommand implements Command { // 聚合LightReceiver private LightReceiver light; public LightOnCommand(LightReceiver light) { this.light = light; } @Override public void execute() { light.on(); } @Override public void undo() { light.off(); } } ``` ```java public class LightOffCommand implements Command { // 聚合LightReceiver private LightReceiver light; public LightOffCommand(LightReceiver light) { this.light = light; } @Override public void execute() { light.off(); } @Override public void undo() { light.on(); } } ``` ```java /** * 用于初始化每个按钮 */ public class NoCommand implements Command { @Override public void execute() { System.out.println("do nothing..."); } @Override public void undo() { System.out.println("do nothing..."); } } ``` `RemoteController调用者` ```java public class RemoteController { // 开命令数组 private Command[] onCommands; // 关命令数组 private Command[] offCommands; // 撤销命令位置 private int no; // 撤销命令是否为按下命令 private Boolean isOn; /** * 初始化 */ public RemoteController() { this.onCommands = new Command[5]; this.offCommands = new Command[5]; for (int i = 0; i < 5; i++) { onCommands[i] = new NoCommand(); offCommands[i] = new NoCommand(); } } /** * 设置一行中的按钮 * @param no * @param onCommand * @param offCommand */ public void setCommand(int no,Command onCommand,Command offCommand) { onCommands[no] = onCommand; offCommands[no] = offCommand; } /** * 开按钮按下 * @param no */ public void onButtonPushed(int no){ // 调用按钮方法 onCommands[no].execute(); // 记录撤销按钮 this.no = no; isOn = true; } /** * 关按钮按下 * @param no */ public void offButtonPushed(int no) { // 调用按钮方法 offCommands[no].execute(); // 记录撤销按钮 this.no = no; isOn = false; } public void undo() { if (isOn) { onCommands[no].undo(); } else { offCommands[no].undo(); } isOn = !isOn; } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 接收者 LightReceiver lightReceiver = new LightReceiver(); CurtainReceiver curtainReceiver = new CurtainReceiver(); // 命令 Command lightOnCommand = new LightOnCommand(lightReceiver); Command lightOffCommand = new LightOffCommand(lightReceiver); Command curtainOnCommand = new CurtainOnCommand(curtainReceiver); Command curtainOffCommand = new CurtainOffCommand(curtainReceiver); // 执行者 RemoteController remoteController = new RemoteController(); remoteController.setCommand(0,lightOnCommand,lightOffCommand); remoteController.setCommand(1,curtainOnCommand,curtainOffCommand); System.out.print("开灯按钮\t"); remoteController.onButtonPushed(0); System.out.print("撤销按钮\t"); remoteController.undo(); System.out.print("撤销按钮\t"); remoteController.undo(); System.out.print("关灯按钮\t"); remoteController.offButtonPushed(0); System.out.println("------------------"); System.out.print("开窗帘按钮\t"); remoteController.onButtonPushed(1); System.out.print("撤销按钮 \t"); remoteController.undo(); System.out.print("撤销按钮 \t"); remoteController.undo(); System.out.print("关窗帘按钮\t"); remoteController.offButtonPushed(1); } } ``` **个人优化方案(仅供参考):** ![improve](command/src/com/xy/improve.png) ## 命令模式的注意事项和细节 - 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute(方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。 - 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令。 - 容易实现对请求的撤销和重做。 - **命令模式不足**:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意。 - **空命令也是一种设计模式,它为我们省去了判空的操作**。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。 - 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD (DOS命令)订单的**撤销/恢复、触发-反馈机制**。 # 设计模式——访问者模式 ## 模式介绍 - 访问者模式(VisitorPatterm),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 - 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题。 - 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。 - 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决。 ## UML类图 ![visitor](visitor/src/com/xy/visitor.png) **类图解析:** - **Visitor**:是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作。 - **ConcreteVisitor** :是一个具体的访问值实现每个有Visitor 声明的操作,是每个操作实现的部分。 - **ObjectStructure**:能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素。 - **Element**:定义一个accept方法,接收一个访问者对象。 - **ConcreteElement**:为具体元素,实现了accept 方法。 ## 访问者模式案例 背景介绍:校园歌手海选环节,观众根据歌手表现,(歌手的受众人群可以查看)可以投出晋级和淘汰选择 ![visitor1](visitor/src/com/xy/visitor1.png) `Action及其子类Success、Fail:` ```java public abstract class Action { // 工具观众得到测评结果 public abstract void getResult(Spectator spectator); } ``` ```java public class Success extends Action{ @Override public void getResult(Spectator spectator) { System.out.println(spectator.getSex() + "\t" + spectator.getName() + "\t给出晋级..."); } } ``` ```java public class Fail extends Action{ @Override public void getResult(Spectator spectator) { System.out.println(spectator.getSex() + "\t" + spectator.getName() + "\t给出淘汰..."); } } ``` `Spectator及其子类Male、Female` ```java public abstract class Spectator { private String name; private String sex; public Spectator(String name, String sex) { this.name = name; this.sex = sex; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } // 提供一个方法,让访问者可以访问 public abstract void accept(Action action); } ``` ```java public class Male extends Spectator { public Male(String name, String sex) { super(name, sex); } @Override public void accept(Action action) { action.getResult(this); } } ``` ```java public class Female extends Spectator { public Female(String name, String sex) { super(name, sex); } @Override public void accept(Action action) { action.getResult(this); } } ``` `ObjectStructure类` ```java public class ObjectStructure { // 维护集合 private List spectators = new ArrayList<>(); /** * 添加 * @param spectator */ public void attach(Spectator spectator) { spectators.add(spectator); } /** * 删除 * @param spectator */ public void detach(Spectator spectator) { spectators.remove(spectator); } /** * 显示 * @param action */ public void display(Action action) { spectators.forEach(spectators.forEach(spectator -> spectator.accept(action))); } } ``` `Client测试类` ``` public class Client { public static void main(String[] args) { ObjectStructure successPersons = new ObjectStructure(); ObjectStructure failPersons = new ObjectStructure(); for (int i = 0; i < 20; i++) { Spectator spectator; if (i % 2 == 0) { spectator = new Male(i + "号 ","男"); } else { spectator = new Female(i + "号 ","女"); } if (i % 2 == 0) { successPersons.attach(spectator); } else { failPersons.attach(spectator); } } Action success = new Success(); Action fail = new Fail(); System.out.println("-----success-----"); successPersons.display(success); System.out.println("------fail------"); failPersons.display(fail); } } ``` 在此基础上在添加一个Wait类,即观众可以思考后在选择 ![visitor2](visitor/src/com/xy/visitor2.png) 无需修改其他类,只需扩展Action类即可,代码如下 ```java public class Wait extends Action { @Override public void getResult(Spectator spectator) { System.out.println(spectator.getSex() + "\t" + spectator.getName() + "\t正在选择中..."); } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { ObjectStructure successPersons = new ObjectStructure(); ObjectStructure failPersons = new ObjectStructure(); ObjectStructure waitPersons = new ObjectStructure(); for (int i = 0; i < 20; i++) { Spectator spectator; if (i % 2 == 0) { spectator = new Male(i + "号 ","男"); } else { spectator = new Female(i + "号 ","女"); } if (i % 3 == 0) { successPersons.attach(spectator); } else if (i % 3 == 1) { failPersons.attach(spectator); } else { waitPersons.attach(spectator); } } Action success = new Success(); Action fail = new Fail(); Action wait = new Wait(); System.out.println("-----success-----"); successPersons.display(success); System.out.println("------fail------"); failPersons.display(fail); System.out.println("------wait-------"); waitPersons.display(wait); } } ``` ## 应用案例的小结-双分派 - double dispatch(双分派)则在选择一个方法的时候,不仅仅要根据消息接收者(receiver) 的运行时型别(Run time type),还要根据参数的运行时型别(Run time type)。 ## 访问者模式的注意事项和细节 - 优点 - 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高。 - 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统。 - 缺点 - 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难。 - 如果一个系统有**比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的**。 # 设计模式——迭代器模式 ## 模式介绍 - 迭代器模式( Iterator Pattern)是**常用的设计模式**,属于行为型模式。 - 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。 - 迭代器模式,**提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示**,即:不暴露其内部的结构。 ## UML类图 ![iterator](iterator/src/com/xy/iterator.png) **类图解析:** - **Iterator** :迭代器接口, 是系统提供,含义hasNext, next, remove。 - **ConcreteIterator**:具体的迭代器类,管理迭代。 - **Aggregate**:一个统一的聚合接口, 将客户 端和具体聚合解耦。 - **ConcreteAggreage**:具体的聚合持有对象集合,并提供 一个方法,返回一个迭代器,该迭 代器可以正确遍历集合。 - **Client**:客户端,通过 Iterator和Aggregate 依赖子类。 ## 迭代器模式案例 背景介绍:在大学中有许多学院,使用迭代器模式对学院及其专业信息进行输出 ![college](iterator/src/com/xy/college.png) `Department专业类` ```java public class Department { private String name; private String desc; public Department(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } ``` `Iterator实现子类ComputerCollegeIterator、InfoCollegeIterator` ```java public class ComputerCollegeIterator implements Iterator { // 数组存放 private Department[] departments; int position = 0; public ComputerCollegeIterator(Department[] departments) { this.departments = departments; } @Override public boolean hasNext() { if (position >= departments.length || departments[position] == null) { return false; } return true; } @Override public Department next() { Department department = departments[position]; position++; return department; } /** * 空实现 */ @Override public void remove() { } } ``` ```java public class InfoCollegeIterator implements Iterator { // 以List存放 private List departments; int index = -1; public InfoCollegeIterator(List departments) { this.departments = departments; } @Override public boolean hasNext() { if (index >= departments.size() - 1) { return false; } return true; } @Override public Department next() { index++; return departments.get(index); } } ``` `College接口及其实现子类ComputerCollege、InfoCollege` ```java public interface College { /** * 获取名字 * @return */ String getName(); /** * 添加系的方法 * @param name * @param desc */ void addDepartment(String name,String desc); Iterator creteIterator(); } ``` ```java public class ComputerCollege implements College{ private Department[] departments; private int numOfDepartment = 0; // 对象个数 private String name; @Override public String getName() { return name; } public ComputerCollege() { departments = new Department[5]; addDepartment("java专业","java专业"); addDepartment("Python专业","Python专业"); addDepartment("PHP专业","PHP专业"); addDepartment("Go专业","Go专业"); addDepartment("C++专业","C++专业"); name = "计算机学院"; } @Override public void addDepartment(String name, String desc) { Department department = new Department(name, desc); departments[numOfDepartment++] = department; } @Override public Iterator creteIterator() { return new ComputerCollegeIterator(departments); } } ``` ```java public class InfoCollege implements College { private List infoColleges; private String name; public InfoCollege() { name = "信息工程学院"; infoColleges = new ArrayList<>(); addDepartment("信息安全专业","信息安全专业"); addDepartment("网络安全专业","网络安全专业"); addDepartment("服务器专业","服务器专业"); } @Override public String getName() { return name; } @Override public void addDepartment(String name, String desc) { infoColleges.add(new Department(name, desc)); } @Override public Iterator creteIterator() { return new InfoCollegeIterator(infoColleges); } } ``` `Output信息输出类` ```java public class Output { // 学院集合 private List collegeList; public Output(List collegeList) { this.collegeList = collegeList; } /** * 输出各个学院的系 */ public void printCollege() { collegeList.forEach(college -> { System.out.println("-------"+college.getName()+"-------"); printDepartment(college.creteIterator()); }); } /** * 学院输出系 */ private void printDepartment(Iterator iterator) { while (iterator.hasNext()) { System.out.println(iterator.next().getName()); } } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 创建学院 ComputerCollege computerCollege = new ComputerCollege(); InfoCollege infoCollege = new InfoCollege(); // 创建学院集合 ArrayList colleges = new ArrayList<>(); colleges.add(computerCollege); colleges.add(infoCollege); // 输出信息类 Output output = new Output(colleges); // 输出 output.printCollege(); } } ``` ## 迭代器模式的注意事项和细节 - 优点 - 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。 - 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。 - 提供了一种**设计思想**,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把**管理对象集合和遍历对象集合的责任分开**,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。 - 当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式。 - 缺点 - 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类 # 设计模式——观察者模式 ## 模式介绍 观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。 ## UML类图 ![Observer](observer/src/com/xy/Observer.png) **类图解析:** - **Subject**:主题接口类,一个主题对应多个观察者,有注册、删除、通知方法 - **ConcreteSubject**:主题具体实现类 - **Observer**:观察者接口类, 有更新方法 - **ConcreteObserver**:观察者具体实现类 ## 观察者模式案例 背景介绍:某站有许多up主,也有许多user用户,用户可以关注up,获取up的更新推送 ![uploader](observer/src/com/xy/uploader.png) `User接口类` ```java public interface User { /** * 更新推送 */ void update(); /** * 获取名字 * @return */ String getName(); } ``` `Uploader接口类` ```java public interface Uploader { /** * 新增粉丝 */ void followFan(User fan); /** * 粉丝取关 */ void unfollowFan(User fan); /** * 添加推送 */ void notifyToFans(); } ``` `Uploader实现类BilibiliUploader` ```java public class BilibiliUploader implements Uploader { private List fans = new ArrayList<>(); private String name; private Object video; public BilibiliUploader(String name) { this.name = name; } @Override public void followFan(User fan) { System.out.println(fan.getName() + "关注了您..."); fans.add(fan); } @Override public void unfollowFan(User fan) { fans.add(fan); } /** * 更新作品 */ public void upload(Object video) { this.video = video; notifyToFans(); } @Override public void notifyToFans() { fans.forEach(User::update); } public String getName() { return name; } public Object getVideo() { return video; } } ``` `User实现类UserForPC、UserForPhone` ```java public class UserForPC implements User { private String name; private BilibiliUploader bilibiliUploader; public UserForPC(String name) { this.name = name; } /** * 关注 */ public void followUp(BilibiliUploader bilibiliUploader) { this.bilibiliUploader = bilibiliUploader; bilibiliUploader.followFan(this); } public void unFollow(BilibiliUploader bilibiliUploader){ // 空 } @Override public void update() { System.out.println("登录通知 你关注的Up主【" + bilibiliUploader.getName() + "】更新了"+ bilibiliUploader.getVideo()); } @Override public String getName() { return name; } } ``` ```java public class UserForPhone implements User { private String name; private BilibiliUploader bilibiliUploader; public UserForPhone(String name) { this.name = name; } public void followUp(BilibiliUploader bilibiliUploader) { this.bilibiliUploader = bilibiliUploader; bilibiliUploader.followFan(this); } public void unFollow(BilibiliUploader bilibiliUploader) { // 空 } @Override public void update() { System.out.println("弹窗通知 你关注的Up主【" + bilibiliUploader.getName() + "】更新了" + bilibiliUploader.getVideo()); } @Override public String getName() { return name; } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { BilibiliUploader up = new BilibiliUploader("罗老师"); UserForPC jack = new UserForPC("jack"); UserForPhone tom = new UserForPhone("tom"); System.out.println("----用户点击关注----"); jack.followUp(up); tom.followUp(up); System.out.println("-----up更新视频-----"); up.upload("张三XXX"); } } ``` # 设计模式——中介者模式 ## 模式介绍 - 中介者模式(Mediator Pattern),用一个**中介对象来封装一系列的对象交互**。中介者使各个对象不需要显式地相互引用,从而使其**耦合松散**,而且可以独立地改变它们之间的交互。 - 中介者模式属于**行为型模式**,使代码易于维护。 - 比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的中介者,在前后端交互时起到了中间人的作用 ## UML类图 ![mediator](mediator/src/com/xy/mediator.png) **类图解析:** - **Mediator**就是抽象中介者,定义了同事对象到中介者对象的接口 - **Colleague**是抽象同事类 - **ConcreteMediator**具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,即以一个集合来管理 - **HashMap**,并接受某个同事对象消息,完成相应的任务 - **ConcreteColleague**具体的同事类,会有很多,每个同事只知道自己的行为,而不了解其他同事类的行为(方法), 但是他们都依赖中介者 对象 ## 中介者模式案例 背景介绍:现代智能家居,根据用户需求制定智能家居相互间的联动效果:如在本案例中闹钟响起后,咖啡机会煮咖啡,并打开TV,观看热点新闻,咖啡煮好后,拉起窗帘并关闭TV... ![mediator1](mediator/src/com/xy/mediator1.png) `Mediator抽象类(智能家居调控)` ```java public abstract class Mediator { public abstract void Register(String colleagueName, Colleague colleague); public abstract void GetMessage(int stateChange, String colleagueName); public abstract void SendMessage(); } ``` `Colleague(智能设备抽象类)` ```java public abstract class Colleague { private Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public Mediator GetMediator() { return this.mediator; } public abstract void SendMessage(int stateChange); } ``` `Mediator子类ConcreteMediator` ```java public class ConcreteMediator extends Mediator { private HashMap colleagueMap; private HashMap interMap; public ConcreteMediator() { colleagueMap = new HashMap(); interMap = new HashMap(); } @Override public void Register(String colleagueName, Colleague colleague) { colleagueMap.put(colleagueName, colleague); // TODO Auto-generated method stub if (colleague instanceof Alarm) { interMap.put("Alarm", colleagueName); } else if (colleague instanceof CoffeeMachine) { interMap.put("CoffeeMachine", colleagueName); } else if (colleague instanceof TV) { interMap.put("TV", colleagueName); } else if (colleague instanceof Curtains) { interMap.put("Curtains", colleagueName); } } @Override public void GetMessage(int stateChange, String colleagueName) { if (colleagueMap.get(colleagueName) instanceof Alarm) { if (stateChange == 0) { ((CoffeeMachine) (colleagueMap.get(interMap .get("CoffeeMachine")))).StartCoffee(); ((TV) (colleagueMap.get(interMap.get("TV")))).StartTv(); } else if (stateChange == 1) { ((TV) (colleagueMap.get(interMap.get("TV")))).StopTv(); } } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) { ((Curtains) (colleagueMap.get(interMap.get("Curtains")))).UpCurtains(); } else if (colleagueMap.get(colleagueName) instanceof TV) { } else if (colleagueMap.get(colleagueName) instanceof Curtains) { } } @Override public void SendMessage() { } } ``` `Colleague子类Alarm、TV、CoffeeMachine、Curtains` ```java public class Alarm extends Colleague { public Alarm(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } public void SendAlarm(int stateChange) { SendMessage(stateChange); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } } ``` ```java public class TV extends Colleague { public TV(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } public void StartTv() { System.out.println("It's time to StartTv!"); } public void StopTv() { System.out.println("StopTv!"); } } ``` ```java public class CoffeeMachine extends Colleague { public CoffeeMachine(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } public void StartCoffee() { System.out.println("It's time to startcoffee!"); } public void FinishCoffee() { System.out.println("After 5 minutes!"); System.out.println("Coffee is ok!"); SendMessage(0); } } ``` ```java public class Curtains extends Colleague { public Curtains(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } public void UpCurtains() { System.out.println("I am holding Up Curtains!"); } } ``` `ClientTest测试类` ```java public class ClientTest { public static void main(String[] args) { // 智能设备中介者 Mediator mediator = new ConcreteMediator(); // 添加智能设备到管中介中 Alarm alarm = new Alarm(mediator, "alarm"); CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine"); Curtains curtains = new Curtains(mediator, "curtains"); TV tV = new TV(mediator, "TV"); // 发送消息 alarm.SendAlarm(0); coffeeMachine.FinishCoffee(); alarm.SendAlarm(1); } } ``` ## 中介者模式的优缺点 **优点** - 通过将中介者支持的**对象从系统中解耦,增加对象的复用性**。 - 通过将控制逻辑集中,**简化了系统的维护**。 - 简化以及减少系统中对象之间发送消息的变换。 **缺点** - 中介者常用于协调相关的GUI组件。 - 中介者模式的一个缺点是,如果**设计不当,中介者对象本身会变得过于复杂**。 # 设计模式——备忘录模式 ## 模式介绍 - 备忘录模式(Memento Pattern)在**不破坏封闭的前提下,捕获一个对象的内部状态**,并在该对象之外保存这个状态。这样以后就可将该**对象恢复到原先保存的状态**。 - 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。 - 备忘录模式属于行为型模式 ## UML类图 ![memento](memento/src/com/xy/memento.png) **类图解析:** - Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。 - Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。 - Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。 ## 备忘录模式案例 背景介绍: 以上面的UML为例,设计一个小游戏存档恢复系统 `Originator类` ```java public class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } /** * 保存状态 */ public Memento save() { return new Memento(getState()); } /** * 恢复状态 */ public void resume(Memento memento) { state = memento.getState(); } } ``` `Memento类` ```java public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } } ``` `Caretaker类` ```java public class Caretaker { private List mementos = new ArrayList<>(); /** * 添加状态 */ public void addState(Memento memento) { System.out.println("添加存档:" + memento.getState()); mementos.add(memento); } /** * 获取状态 */ public Memento getState(int index) { Memento memento = mementos.get(index); System.out.println("恢复存档:" + memento.getState()); return memento; } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 游戏开始 Originator originator = new Originator(); // 游戏存档 Caretaker caretaker = new Caretaker(); // 游戏进度 originator.setState("剩余100点血"); System.out.println("++通过第一关,前往第二关++"); caretaker.addState(originator.save()); originator.setState("剩余75点血"); System.out.println("++通过第二关,前往第三关++"); caretaker.addState(originator.save()); originator.setState("战败~~"); caretaker.addState(originator.save()); System.out.println("++请选择恢复存档店++"); System.out.println("++1.第二关开始100点血 2.第三关开始75点血++"); System.out.print("请选择:"); Scanner scanner = new Scanner(System.in); switch (scanner.next()) { case "1": System.out.println("恢复第二关开始存档点"); originator.resume(caretaker.getState(0)); break; case "2": System.out.println("恢复第二关开始存档点"); originator.resume(caretaker.getState(1)); break; default: System.out.println("退出~"); break; } } } ``` ## 备忘录模式优缺点 **优点** - 保存与被保存状态处于**关键对象的外面,有助于维护内聚**。 - 保存关键对象的数据封装。 - 提供容易实现的**恢复能力**。 **缺点** - 备忘与用于保存状态。 - 使用备忘录,**保存和恢复状态可能相当耗时**。 - 在Java系统中考虑使用**序列化(Serialization 即单例模式)来保存对象的状态**。 # 设计模式——解释器模式 ## 模式介绍 - 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器。 - 解释器模式(Interpreter Pattern) :是**指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)**。 - 应用场景 - 应用可以将一一个需要解释执行的语言中的句子表示为一个抽象语法树 - 一些重复出现的问题可以用一种简单的语言来表达。 - 一个简单语法需要解释的场景。 - 这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等。 ## UML类图 ![interpreter](interpreter/src/com/xy/interpreter.png) **类图解析:** - Context:是环境角色,含有解释器之外的全局信息。 - AbstractExpression:抽象表达式,声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享。 - TerminalExpression:为终结符表达式,实现与文法中的终结符相关的解释操作。 - NonTermialExpression:为非终结符表达式,为文法中的非终结符实现解释操作。 说明:输 入Context 和 TerminalExpression信息通过Client输入即可 ## 解释器模式案例 背景介绍:加减运算器,运算包含加法和减法如输入 a+b-c 会先给a、b、c赋值,最终得出结果 ![calculate](interpreter/src/com/xy/calculate.png) `Expression抽象类` ```java /** * 抽象类表达式,通过Hashmap键值对,可以获取到变量的值 */ public abstract class Expression { /** * 解释公式和数,key,公式表达式中的参数【a,b,c】,value就是具体值 * { a = 10, b = 20} */ public abstract int interpreter(HashMap var); } ``` `VarExpression终结符表达式` ```java public class VarExpression extends Expression { private String key; public VarExpression(String key) { this.key = key; } @Override public int interpreter(HashMap var) { return var.get(this.key); } } ``` `SymbolExpression加减法运算抽象类` ```java public class SymbolExpression extends Expression { protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right) { this.left = left; this.right = right; } @Override public int interpreter(HashMap var) { // TODO Auto-generated method stub return 0; } } ``` `SymbolExpression子类AddExpression、SubExpression` ```java public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } public int interpreter(HashMap var) { // 通过多态调用VarExpression return super.left.interpreter(var) + super.right.interpreter(var); } } ``` ```java public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } public int interpreter(HashMap var) { // 通过多态调用VarExpression return super.left.interpreter(var) - super.right.interpreter(var); } } ``` `Calculator计算类` ```java public class Calculator { // 定义表达式 private Expression expression; // 构造函数传参,并解析 public Calculator(String expStr) { // 安排运算先后顺序 Stack stack = new Stack<>(); char[] charArray = expStr.toCharArray(); Expression left = null; Expression right = null; for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': // 加法 left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i]));// 获取右数值 stack.push(new AddExpression(left, right));//嵌套 break; case '-': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } this.expression = stack.pop(); } public int run(HashMap var) { return this.expression.interpreter(var); } } ``` `ClientTest测试类` ```java public class ClientTest { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub String expStr = getExpStr(); HashMap var = getValue(expStr); Calculator calculator = new Calculator(expStr); System.out.println("运算结果:" + expStr + "=" + calculator.run(var)); } // 获得表达式 public static String getExpStr() throws IOException { System.out.print("请输入表达式:"); return (new BufferedReader(new InputStreamReader(System.in))).readLine(); } // 获得值映射 public static HashMap getValue(String expStr) throws IOException { HashMap map = new HashMap<>(); for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') { if (!map.containsKey(String.valueOf(ch))) { System.out.print("请输入" + String.valueOf(ch) + "的值:"); String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch), Integer.valueOf(in)); } } } return map; } } ``` ## 解释器模式的优缺点 **优点** - 将每一个语法规则表达成一个类,使得语言容易实现。 - 因为语法由类表达,你可以轻易的改变和扩展该语言。 - 通过在类结构中添加方法,可以添加解释之外的新行为。 **缺点** - 当语法规则的数目很大时,这个模式可能变得笨重。这种情况下,一个解析器/编译器的生成可能更合适。 # 设计模式——状态模式 ## 模式介绍 - 状态模式(StatePattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。 - 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。 ## UML类图 ![state](state/src/com/xy/state.png) **类图解析:** - **Context**类为环境角色,用于维护State实例,这个实例定义当前状态。 - **State**是抽象状态角色,定义一个接口封装与Context的一个特点接口相关行为。 - **ConcreteState**具体的状态角色,每个子类实现一个与Context的一个状态相关行为。 ## 状态模式案例 背景介绍:抽奖系统,设置奖品数量,用户通过消费 50积分抽奖可以抽奖,未抽中继续消费50积分...如下图所示: ![img](state/src/com/xy/img.png) ![app](state/src/com/xy/app.png) `State抽象类及其实现子类` ```java public abstract class State { /** * 扣除积分 -50 */ public abstract void deductMoney(); /** *抽奖中 */ public abstract boolean raffle(); /** * 发放奖品 */ public abstract void dispensePrize(); } ``` ```java public class NoRaffleState extends State { // 初始化时传入活动引用,扣除积分后改变其状态 RaffleActivity activity; public NoRaffleState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("扣除50积分成功,您可以抽奖了"); activity.setState(activity.getCanRaffleState()); } @Override public boolean raffle() { System.out.println("扣了积分才能抽奖喔!"); return false; } @Override public void dispensePrize() { System.out.println("不能发放奖品"); } } ``` ```java public class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("已经扣取过了积分"); } @Override public boolean raffle() { System.out.println("正在抽奖,请稍等!"); Random r = new Random(); int num = r.nextInt(10); // 10%中奖机会 if(num == 0){ // 改变活动状态为发放奖品 activity.setState(activity.getDispenseState()); return true; }else{ System.out.println("很遗憾没有抽中奖品!"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); return false; } } @Override public void dispensePrize() { System.out.println("没中奖,不能发放奖品"); } } ``` ```java public class DispenseState extends State { // 初始化时传入活动引用,发放奖品后改变其状态 RaffleActivity activity; public DispenseState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("不能扣除积分"); } @Override public boolean raffle() { System.out.println("不能抽奖"); return false; } @Override public void dispensePrize() { int count = activity.getCount(); if(count > 0){ System.out.println("恭喜中奖了"); // 改变状态 activity.setState(count == 1? activity.getDispensOutState() : activity.getNoRafflleState()); }else{ System.out.println("很遗憾,奖品发送完了"); System.out.println("退还积分..."); // 改变状态为奖品发送完毕 activity.setState(activity.getDispensOutState()); } } } ``` ```java public class DispenseOutState extends State { // 初始化时传入活动引用 RaffleActivity activity; public DispenseOutState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("奖品发送完了,请下次再参加"); } @Override public boolean raffle() { System.out.println("奖品发送完了,请下次再参加"); return false; } @Override public void dispensePrize() { System.out.println("奖品发送完了,请下次再参加"); } } ``` `CanRaffleState抽奖类(控制状态切换)` ```java public class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("已经扣取过了积分"); } @Override public boolean raffle() { System.out.println("正在抽奖,请稍等!"); Random r = new Random(); int num = r.nextInt(10); // 10%中奖机会 if(num == 0){ // 改变活动状态为发放奖品 activity.setState(activity.getDispenseState()); return true; }else{ System.out.println("很遗憾没有抽中奖品!"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); return false; } } @Override public void dispensePrize() { System.out.println("没中奖,不能发放奖品"); } } ``` `ClientTest测试类` ```java public class ClientTest { public static void main(String[] args) { // TODO Auto-generated method stub // 创建活动对象,奖品池有5个奖品 RaffleActivity activity = new RaffleActivity(1); // 模拟抽奖 for (int i = 0; i < 20; i++) { System.out.println("--------第" + (i + 1) + "次抽奖----------"); // 参加抽奖,第一步点击扣除积分 activity.debuctMoney(); // 第二步抽奖 activity.raffle(); } } } ``` ## 状态模式的注意事项和细节 - 代码有很强的**可读性**。状态模式将每个状态的行为封装到对应的一个类中**方便维护**。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else 语句,而且容易出错。 - **符合“开闭原则”。容易增删状态**。 - 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度。 - 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式。 # 设计模式——策略模式 ## 模式介绍 - 策略模式(Strategy Patterm)中,定义**算法族(策略组)**,分别封装起来,让他们之间可以互相替换,此模式让**算法的变化独立于使用算法的客户**。 - 这算法体现了几个设计原则, - **把变化的代码从不变的代码中分离出来**; - **针对接口编程而不是具体类(定义了策略接口)**; - **多用组合/聚合,少用继承(客户通过组合方式使用策略)**。 ## UML类图 ![strategy](strategy/src/com/xy/strategy.png) ## 策略模式案例 背景介绍:定义一个鸭子类鸭子原本有游泳、飞翔、发声等方法,但并不是所有鸭子都会有飞翔、发声方法(如玩具鸭、诱饵鸭等)所以将飞翔和发声抽离出来做成一个抽象类,并聚合到鸭子类中使需要实现得类调用具体方法 ![duck1](strategy/src/com/xy/duck1.png) `FlyBehavior接口类类及其子类` ```java public interface FlyBehavior { void fly(); } ``` ```java public class FlyNoWay implements FlyBehavior{ @Override public void fly() { System.out.println("我不能飞翔..."); } } ``` ```java public class FlyWithWings implements FlyBehavior{ @Override public void fly() { System.out.println("我能飞翔..."); } } ``` `Quack接口类类及其子类` ```java public interface QuackBehavior { void quack(); } ``` ```java public class Quack implements QuackBehavior{ @Override public void quack() { System.out.println("我嘎嘎叫..."); } } ``` ```java public class Squeak implements QuackBehavior{ @Override public void quack() { System.out.println("鸭子吱吱叫..."); } } ``` ```java public class MuteQuack implements QuackBehavior{ @Override public void quack() { System.out.println("我不会叫..."); } } ``` `Duck抽象类及其实现子类` ```java public abstract class Duck { // 飞行行为 protected FlyBehavior flyBehavior; // 发声行为 protected QuackBehavior quackBehavior; /** * 显示品种 */ abstract void display(); /** * 游泳 */ public void swim() { System.out.println("所有鸭子都可以游泳..."); } /** * 发声 */ public void performQuack() { if (quackBehavior != null) { quackBehavior.quack(); } } /** * 飞翔 */ public void performFly() { if (flyBehavior != null) { flyBehavior.fly(); } } public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } } ``` ```java public class MallardDuck extends Duck{ public MallardDuck() { flyBehavior = new FlyWithWings(); quackBehavior = new Squeak(); } @Override public void display() { System.out.println("我是绿头鸭..."); } } ``` ```java public class RedHeadDuck extends Duck{ public RedHeadDuck() { flyBehavior = new FlyNoWay(); quackBehavior = new Quack(); } @Override public void display() { System.out.println("我是红头鸭..."); } } ``` ```java public class RubberDuck extends Duck{ public RubberDuck() { flyBehavior = new FlyNoWay(); quackBehavior = new MuteQuack(); } @Override public void display() { System.out.println("我是玩具鸭..."); } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { Duck mallardDuck = new MallardDuck(); System.out.println("----绿头鸭----"); mallardDuck.display(); mallardDuck.performQuack(); mallardDuck.performFly(); Duck redHeadDuck = new RedHeadDuck(); System.out.println("----红头鸭----"); redHeadDuck.display(); redHeadDuck.performQuack(); redHeadDuck.performFly(); Duck rubberDuck = new RubberDuck(); System.out.println("----玩具鸭----"); rubberDuck.display(); rubberDuck.performFly(); rubberDuck.performQuack(); System.out.println("玩具鸭添加发声"); redHeadDuck.setQuackBehavior(new Quack()); redHeadDuck.performQuack(); } } ``` ## 策略模式的注意事项和细节 - 策略模式的关键是:分析项目中**变化部分与不变部分**。 - 策略模式的核心思想是:**多用组合/聚合少用继承**;用行为类组合,而不是行为的继承。更有弹性体现了“**对修改关闭,对扩展开放**”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if.else if.else )。 - 提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它**易于切换、易于理解、易于扩展**。 - **需要注意的是**:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞。 # 设计模式——职责链模式 ## 模式介绍 - 职责链模式(Chain Of Responsibility Pattern),又叫责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。 - 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。 - 这种类型的设计模式属于行为型模式 ## UML类图 ![chain](resposibilitychain/src/com/xy/chain.png) **类图解析:** - **Handler**:抽象的处理者,定义了一个处理请求的接口,同时含义另外Handler。 - **ConcreteHandler**A,B是具体的处理者,处理它自己负责的请求,可 以访问它的后继者(即下一个处理者),如果可以处理当前请求,则处理,否则就将该请求交个后继者去处理,从而形成一个职责链。 - **Request**,含义很多属性, 表示一个请求。 ## 职责链模式案例 采购员采购教学器材 - 如果金额小于等于5000,由教学主任审批(0<=x<=5000) - 如果金额小于等于10000,由院长审批(5000 30000) { System.out.println("请求编号:" + purchaseRequest.getId() + ", 请求金额:" + purchaseRequest.getPrice() + ", 请求类型:" + purchaseRequest.getType()+ ", 被" + name + "处理了..."); } else { approver.processRequest(purchaseRequest); } } } ``` `PurchaseRequest(购教学器材类)` ```java public class PurchaseRequest { private int type = 0; // 请求类型 private double price = .0d; // 请求金额 private int id = 0;// 请求编号 public PurchaseRequest(int type, double price, int id) { this.type = type; this.price = price; this.id = id; } public int getType() { return type; } public double getPrice() { return price; } public int getId() { return id; } } ``` `Client测试类` ```java public class Client { public static void main(String[] args) { // 采购器材请求 PurchaseRequest purchaseRequest1 = new PurchaseRequest(1, 3000, 1); PurchaseRequest purchaseRequest2 = new PurchaseRequest(2, 9000, 2); PurchaseRequest purchaseRequest3 = new PurchaseRequest(3, 24000, 3); PurchaseRequest purchaseRequest4 = new PurchaseRequest(4, 100000, 4); // 所有处理类 Approver departmentApprover = new DepartmentApprover("教导主任"); Approver collegeApprover = new CollegeApprover("学院院长"); Approver viceSchoolMasterApprover = new ViceSchoolMasterApprover("副校长"); Approver schoolMasterApprover = new SchoolMasterApprover("校长"); // 设置职责链 departmentApprover.setApprover(collegeApprover); collegeApprover.setApprover(viceSchoolMasterApprover); viceSchoolMasterApprover.setApprover(schoolMasterApprover); // 处理请求 departmentApprover.processRequest(purchaseRequest1); departmentApprover.processRequest(purchaseRequest2); departmentApprover.processRequest(purchaseRequest3); departmentApprover.processRequest(purchaseRequest4); } } ``` ## 职责链模式的注意事项和细节 - 将请求和处理分开,实现解耦,提高系统的灵活性。 - 简化了对象,使对象不需要知道链的结构。 - 性能会受到影响,特别是在**链比较长的时候**,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext)方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。 - 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂。 - 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、JavaWeb中Tomcat对Encoding的处理、拦截器。