# 设计模式 设计模式是对许多业务和算法的抽象,因此学习起来具有一定的难度。短时间内掌握是不可能的,需要通过不断的实践和理解来积累经验。对于已经在职场上工作,但后期才开始学习设计模式的人来说,可能会面临更多的挑战。 在学习设计模式时,很多概念趋于理论化,且种类繁多(设计模式总共有23种),如果没有进行系统的梳理,可能很难直接应用到实际场景中。 设计模式中的抽象类通常是接口或抽象类,这些类的实现会根据具体的业务需求进行调整,因而不一定是唯一的实现方式。 在我学习过程中,我试图将所学的内容总结下来,但由于篇幅有限,无法一一列举所有场景,只能举出一些常见的例子。 --- ## 策略模式 ### 常见应用场景 策略模式广泛应用于支付、计算、文件压缩、登录等场景,尤其是在游戏中,如王者荣耀和PUBG的角色处理。值得注意的是,在实际开发中,一种业务场景可能会同时使用多种设计模式,因此,下面的介绍仅为简化并专注于策略模式的核心概念。 ### 策略模式基础介绍 策略模式涉及三个主要角色: 1. **抽象策略(Strategy)** 2. **具体策略(Concrete Strategy)** 3. **上下文(Context)** 其中,`Context` 的概念较为抽象,通常用来持有策略并在运行时动态地切换策略。 ![策略模式结构图](./images/设计模式-v2/image-20250201164040235.png) #### 代码实现 策略模式的实现并不复杂,主要是定义三个角色:抽象策略、具体策略和上下文。为了更清晰地展示,我定义了两个具体策略,每个策略执行不同的操作。然后将策略放入上下文中,最终通过上下文来执行具体策略的方法。 ```java public class BaseStrategy { public static void main(String[] args) { // 创建具体策略 ConcreteStrategy1 concreteStrategy1 = new ConcreteStrategy1(); ConcreteStrategy2 concreteStrategy2 = new ConcreteStrategy2(); // 通过上下文执行策略 new Context(concreteStrategy1).doIt(); new Context(concreteStrategy2).doIt(); } // 抽象策略接口 public interface Strategy { /** * 执行策略方法 */ void method(); } // 具体策略实现1 public static class ConcreteStrategy1 implements Strategy { @Override public void method() { System.out.println("执行具体策略1..."); } } // 具体策略实现2 public static class ConcreteStrategy2 implements Strategy { @Override public void method() { System.out.println("执行具体策略2..."); } } // 上下文类,持有策略并执行 public static class Context { private final Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } /** * 执行策略的方法 */ public void doIt() { strategy.method(); } } } ``` #### 执行结果: ```java 执行具体策略1... 执行具体策略2... ``` ### 示例-游戏角色 需要制作的游戏角色业务需求,基础角色是其他角色的抽象,包括国王、王后、骑士、士兵等,武器是一个接口所有武器都实现这个接口,切换武器(武器有主武器和副武器)可以调用`setPrimaryWeapon`和`setSubWeapon`。 ![image-20250201173433406](./images/设计模式-v2/image-20250201173433406.png) #### 代码实现 ```java public class GameCharacter { public static void main(String[] args) { Character soldier = new Soldier(); soldier.fight(); Character king = new King(); king.setPrimaryWeapon(new AxeWeapon()); king.fight(); } // 武器接口 interface Weapon { String function(); } // 各种武器实现 public static class SwordWeapon implements Weapon { @Override public String function() { return "⚔️ ⚔️ ⚔️"; } } public static class AxeWeapon implements Weapon { @Override public String function() { return "🪓 🪓 🪓"; } } public static class KnifeWeapon implements Weapon { @Override public String function() { return "🔪 🔪 🔪"; } } // 抽象角色类 public abstract static class Character { String name; @Setter private Weapon primaryWeapon; @Setter private Weapon subWeapon; public Character(String name) { this.name = name; // 默认武器配置 this.primaryWeapon = new SwordWeapon(); this.subWeapon = new KnifeWeapon(); } void fight() { if (primaryWeapon != null && subWeapon != null) { String primary = primaryWeapon.function(); String sub = subWeapon.function(); System.out.println(name + ":" + primary + "和" + sub); } else { System.out.println(name + "没有武器!"); } } } // 国王角色 public static class King extends Character { public King() { super("King"); setPrimaryWeapon(new SwordWeapon()); setSubWeapon(new KnifeWeapon()); } } // 士兵角色 public static class Soldier extends Character { public Soldier() { super("Soldier"); setPrimaryWeapon(new AxeWeapon()); setSubWeapon(new SwordWeapon()); } } } ``` #### 运行结果: ```java Soldier:🪓 🪓 🪓和⚔️ ⚔️ ⚔️ King:🪓 🪓 🪓和🔪 🔪 🔪 ``` --- ## 观察者模式 观察者有四个角色,观察者(Observe)和具体观察者(Concrete Observe)、主题(Subject)和具体主题(Concrete Subject),具体观察者和具体主题可以多个。 ![image-20250201175728138](./images/设计模式-v2/image-20250201175728138.png) ### 简单示例 其中主题中有3个方法,添加(或者理解成注册都可以)、移出、通知,差不多都是这三个方法,其中添加可以理解成注册加入总之是往数组中添加观察者。 #### 代码实现 ```java public class ObserverDemo { public static void main(String[] args) { // 定义的主题 News news = new News(); // 观察者 SisterObserver sisterObserver = new SisterObserver(); GirlfriendObserver girlfriendObserver = new GirlfriendObserver(); // 添加观察者 news.addObserver(sisterObserver); news.notifyObservers("添加了妹妹"); System.out.println("\n-----------------分割线-----------------\n"); // 添加女朋友 news.addObserver(girlfriendObserver); news.notifyObservers("添加了女朋友"); System.out.println("\n-----------------分割线-----------------\n"); news.removeObserver(girlfriendObserver); news.notifyObservers("需要和妹妹说点悄悄话,将女朋友移除了,这时女朋友听不见我们说话。。。"); } // 观察者接口 @FunctionalInterface interface Observer { void update(String message); } // 主题接口 interface Subject { void addObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(String message); } // 妹妹观察者 static class SisterObserver implements Observer { @Override public void update(String message) { System.out.println("SisterObserver 接受到消息:" + message); } } // 女朋友观察者 static class GirlfriendObserver implements Observer { @Override public void update(String message) { System.out.println("GirlfriendObserver 接受到消息:" + message); } } // 主题类 static class News implements Subject { private final List observerList = new ArrayList<>(); @Override public void addObserver(Observer observer) { observerList.add(observer); } @Override public void removeObserver(Observer observer) { observerList.remove(observer); } @Override public void notifyObservers(String message) { for (Observer observer : observerList) { observer.update(message); } } } } ``` #### 运行结果: ```java SisterObserver 接受到消息:添加了妹妹 -----------------分割线----------------- SisterObserver 接受到消息:添加了女朋友 GirlfriendObserver 接受到消息:添加了女朋友 -----------------分割线----------------- SisterObserver 接受到消息:需要和妹妹说点悄悄话,将女朋友移除了,这时女朋友听不见我们说话。。。 ``` --- ## 装饰者模式 一共有四个角色。 - 抽象组件:一般是最原始、最基础的对象。 - 具体组件:一般是最原始最基础的实现对象,可以有多个(只是数量不多),可以大致看下`InputStream`UML结构图。 - 装饰角色:继承或者实现**抽象组件**这是最关键的地方。 - 具体装饰角色:可以有多个实现或者继承**装饰角色**。 ### 简单示例 需要做一个抽象的相机功能,相机包含拍照功能(这是肯定的),之后随着业务需求要求相机有美颜功能,在这之后还要有滤镜功能,客户还需要延迟摄影功能。 ![image-20250201185356903](./images/设计模式-v2/image-20250201185356903.png) #### 代码实现 1. 需要定义根组件(Component) 2. 制作最原始的拍照功能,实现根组件 3. 制作延迟摄影,实现根组件 4. 作为相机之外的功能(美颜),需要定义装饰角色方便为以后的组件进行扩展 5. 继承或者实现(装饰者角色),完成美颜 6. 继承或者实现(装饰者角色),完成滤镜 ```java public class CameraDemo { public static void main(String[] args) { TakePhotoCamera takePhotoCamera = new TakePhotoCamera(); takePhotoCamera.operation(); System.out.println("\n-----------------分割线-----------------\n"); BeautyCamera beautyCamera = new BeautyCamera(takePhotoCamera); beautyCamera.operation(); System.out.println("\n-----------------分割线-----------------\n"); FilterCamera filterCamera = new FilterCamera(beautyCamera); filterCamera.operation(); System.out.println("\n-----------------分割线-----------------\n"); TimerCamera timerCamera = new TimerCamera(); timerCamera.setTime(2); timerCamera.operation(); filterCamera = new FilterCamera(beautyCamera); filterCamera.operation(); } // 相机功能 interface Camera { /** * 相机操作 */ void operation(); } // 相机装饰者角色 static abstract class CameraDecorator implements Camera { Camera camera; public CameraDecorator(Camera camera) { this.camera = camera; } } // 相机拍照 static class TakePhotoCamera implements Camera { /** * 相机操作 */ @Override public void operation() { System.out.println("相机拍照。。。"); } } // 相机功能 static class BeautyCamera extends CameraDecorator { public BeautyCamera(Camera camera) { super(camera); } /** * 相机操作 */ @Override public void operation() { camera.operation(); System.out.println("美颜功能。。。"); } } // 滤镜效果 static class FilterCamera extends CameraDecorator { public FilterCamera(Camera camera) { super(camera); } /** * 相机操作 */ @Override public void operation() { camera.operation(); System.out.println("滤镜效果。。。"); } } // 实现相机延迟功能 @Setter static class TimerCamera implements Camera { // 延迟时间 private Integer time = 0; /** * 相机操作 */ @Override public void operation() { System.out.println("开始拍照,延迟时间:" + time + "s"); try { Thread.sleep(time * 1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } ``` #### 运行结果: ```java 相机拍照。。。 -----------------分割线----------------- 相机拍照。。。 美颜功能。。。 -----------------分割线----------------- 相机拍照。。。 美颜功能。。。 滤镜效果。。。 -----------------分割线----------------- 开始拍照,延迟时间:2s 相机拍照。。。 美颜功能。。。 滤镜效果。。。 ``` --- ## 命令模式 **Command**:定义执行请求的接口(或抽象类)。 **ConcreteCommand**:实现命令接口并具体化执行请求的过程,通常还会持有接收者对象。 **Receiver**:具体的对象,完成实际的操作任务。 **Invoker**:调用命令的对象,触发命令的执行。 **Client**:客户端,负责创建并配置命令对象,将命令与接收者绑定,并传递给调用者执行。 --- ## 单例模式 ### 懒汉模式 #### 双重检查锁定(DCL) ```java public class Singleton { private static volatile Singleton instance; private Singleton() { // 构造函数私有化 } public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 instance = new Singleton(); } } } return instance; } } ``` #### 静态内部类方式 利用 **静态内部类**(饿汉式)的特性来实现懒汉模式,并且可以保证线程安全。这种方式只有在调用 `getInstance()` 时才会加载内部类,从而实现懒加载。 ```java public class Singleton { private Singleton() { // 构造函数私有化 } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } } ``` #### 适用场景 - 如果单例对象比较重,且只在程序中某些特定情况下才使用,懒汉模式可以带来性能优势。 - 在低并发环境或单线程环境下,懒汉模式使用较为简单,不会有多线程安全问题。 - 对于高并发且需要频繁访问单例的场景,推荐使用**静态内部类**或**饿汉模式**来避免懒汉模式可能带来的性能问题。 #### 总结 懒汉模式有时为了延迟初始化而牺牲了性能,特别是在多线程环境下。为了保证线程安全,可以采用双重检查锁定(DCL)或静态内部类的方式,而**静态内部类方式是最推荐的方式** ,它避免了同步开销,同时也能确保线程安全。 - **优点**: 1. 延迟加载,节省资源。 2. 内存优化,只有在需要时才实例化。 3. 减少不必要的初始化开销。 - **缺点**: 1. 传统懒汉模式线程不安全(多个线程可能会创建多个实例)。 2. 使用同步机制会有性能开销,特别是在多线程环境下。 3. 代码复杂性较高,容易引入错误。 4. 不适合高并发场景,特别是频繁访问时会影响性能。 ### 饿汉模式 **饿汉模式**(Eager Initialization)是单例模式的一种实现方式,在该方式中,单例实例在类加载时就已经创建完成,而不是在首次使用时才创建。换句话说,**饿汉模式** 的单例实例在类被加载时就会立即初始化,确保了单例模式的唯一性。 ```java public class Singleton { // 在类加载时就初始化单例对象 private static final Singleton INSTANCE = new Singleton(); // 私有构造函数,防止外部通过 new 创建实例 private Singleton() { } // 提供全局访问点 public static Singleton getInstance() { return INSTANCE; } } ``` #### 适用场景 - **简单应用**:适合没有延迟初始化需求的小型应用,或者单例对象的创建非常轻量,不会影响系统性能的场景。 - **实例化不耗费太多资源**:如果单例对象的初始化过程非常轻量级且无须延迟初始化,可以使用饿汉模式。 - **类加载顺序固定**:如果不在乎单例对象在启动时是否初始化,且单例对象的初始化过程没有严重性能损失,可以选择饿汉模式。 #### 总结 - **优点**:简单、线程安全、性能高(没有锁机制)、实现方便。 - **缺点**:不支持延迟加载、可能会浪费资源(如果单例对象不常用时)。 饿汉模式是单例模式中最简单的实现方式,适用于大多数情况下单例对象的创建开销较小,且不需要延迟初始化的场景。如果单例对象的创建非常轻量且不依赖于后期加载时的资源,饿汉模式是一个不错的选择。 ### 枚举类型实现 **枚举类型实现单例模式**(Enum Singleton)是实现单例模式的另一种方式,优点是更加简洁、可靠,并且避免了许多常见的单例实现方式(如懒汉模式和饿汉模式)可能出现的问题。使用枚举类型的单例实现方式自 **Java 5** 引入以来,已经成为了最推荐的单例模式实现方式之一。 **当你不需要懒加载或延迟初始化时,使用枚举实现单例模式是最理想的选择。** ```java public enum Singleton { // 唯一实例 INSTANCE; // 可以添加其他的方法 public void someMethod() { System.out.println("This is a singleton instance method."); } } ``` #### 使用示例 ```java public class Main { public static void main(String[] args) { // 获取单例对象 Singleton singleton = Singleton.INSTANCE; // 调用单例方法 singleton.someMethod(); } } ``` ## 建造者模式 ![image-20250201203116885](./images/设计模式-v2/image-20250201203116885.png) ### 简单示例 比如我们要构建一个复杂的 `Computer`(计算机)对象。我们可以定义一个 `ComputerBuilder`,它将按顺序设置计算机的 CPU、内存、硬盘等组件,最后得到一个完整的 `Computer` 对象。 #### 代码示例 ```java public class CommandDemo { public static void main(String[] args) { ConcreteLowComputerBuilder concreteLowComputerBuilder = new ConcreteLowComputerBuilder(); Director director = new Director(concreteLowComputerBuilder); director.constructComputer(); System.out.println(concreteLowComputerBuilder.getComputer()); System.out.println("\n-----------------分割线-----------------\n"); ConcreteHighComputerBuilder concreteHighComputerBuilder = new ConcreteHighComputerBuilder(); director = new Director(concreteHighComputerBuilder); director.constructComputer(); System.out.println(concreteHighComputerBuilder.getComputer()); } // 构建产品接口 interface ComputerBuilder { /** * 构建 CPU 方法 */ void buildCPU(); /** * 构建 RAM 方法 */ void buildRAM(); /** * 构建 磁盘 方法 */ void buildHardDrive(); /** * 返回产品 * * @return Computer */ Computer getComputer(); } // 产品类 @Data static class Computer { private String CPU; private String RAM; private String hardDrive; } // 构建低端产品 static class ConcreteLowComputerBuilder implements ComputerBuilder { private final Computer computer = new Computer(); /** * 构建 CPU 方法 */ @Override public void buildCPU() { computer.setCPU("Low CPU"); } /** * 构建 RAM 方法 */ @Override public void buildRAM() { computer.setRAM("Low RAM"); } /** * 构建 磁盘 方法 */ @Override public void buildHardDrive() { computer.setHardDrive("Low Driver"); } /** * 返回产品 * * @return Computer */ @Override public Computer getComputer() { return computer; } } // 构建高端产品 static class ConcreteHighComputerBuilder implements ComputerBuilder { private final Computer computer = new Computer(); /** * 构建 CPU 方法 */ @Override public void buildCPU() { computer.setCPU("High CPU"); } /** * 构建 RAM 方法 */ @Override public void buildRAM() { computer.setRAM("High RAM"); } /** * 构建 磁盘 方法 */ @Override public void buildHardDrive() { computer.setHardDrive("High Driver"); } /** * 返回产品 * * @return Computer */ @Override public Computer getComputer() { return computer; } } // 构建者 public static class Director { private final ComputerBuilder builder; public Director(ComputerBuilder builder) { this.builder = builder; } public void constructComputer() { builder.buildCPU(); builder.buildRAM(); builder.buildHardDrive(); } } } ``` #### 运行结果 ```java CommandDemo.Computer(CPU=Low CPU, RAM=Low RAM, hardDrive=Low Driver) -----------------分割线----------------- CommandDemo.Computer(CPU=High CPU, RAM=High RAM, hardDrive=High Driver) ```