> For the complete documentation index, see [llms.txt](https://clloud.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://clloud.gitbook.io/notes/she-ji-mo-shi.md).

# 设计模式

## 设计原则 S.O.L.I.D

### 1.  单一责任原则

The Single Responsibility Principle

> 修改一个类的原因应该只有一个。

换句话说就是让一个类只负责一件事，当这个类需要做过多事情的时候，就需要分解这个类。

如果一个类承担的职责过多，就等于把这些职责耦合在了一起，一个职责的变化可能会削弱这个类完成其它职责的能力。

### 2.  开放封闭原则

The Open Closed Principle

> 类应该对扩展开放，对修改关闭。

扩展就是添加新功能的意思，因此该原则要求在添加新功能时不需要修改代码。

符合开闭原则最典型的设计模式是装饰者模式，它可以动态地将责任附加到对象上，而不用去修改类的代码。

### 3.  里氏替换原则

The Liskov Substitution Principle

> 子类对象必须能够替换掉所有父类对象。

继承是一种 IS-A 关系，子类需要能够当成父类来使用，并且需要比父类更特殊。

如果不满足这个原则，那么各个子类的行为上就会有很大差异，增加继承体系的复杂度。

### 4.  接口分离原则

The Interface Segregation Principle

> 不应该强迫客户依赖于它们不用的方法。

因此使用多个专门的接口比使用单一的总接口要好。

### 5.  依赖倒置原则

The Dependency Inversion Principle

> 高层模块不应该依赖于低层模块，二者都应该依赖于抽象； 抽象不应该依赖于细节，细节应该依赖于抽象。

高层模块包含一个应用程序中重要的策略选择和业务模块，如果高层模块依赖于低层模块，那么低层模块的改动就会直接影响到高层模块，从而迫使高层模块也需要改动。

## 单例

确保一个类只有一个实例，并提供该实例的全局访问点。

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

### 懒汉式 - 非线程安全

```java
public class Singleton1 {
    private static Singleton1 uniqueInstance;

    private Singleton1() {}

    public static Singleton1 getInstance() {
        if (uniqueInstance == null)
            uniqueInstance = new Singleton1();
        return uniqueInstance;
    }
}
```

### 懒汉式 - 线程安全

```java
public class Singleton2 {
    private static Singleton2 uniqueInstance;

    private Singleton2() {}

    // 使用同步方法，避免多线程下创建多个实例
    public static synchronized Singleton2 getInstance() {
        if (uniqueInstance == null)
            uniqueInstance = new Singleton2();
        return uniqueInstance;
    }
}
```

### 双重校验锁

```java
public class Singleton3 {
    // volatile确保当uniqueInstance被实例化时，其他线程能感知到
    private static volatile Singleton3 uniqueInstance;

    private Singleton3() {}

    // 双重校验锁先判断 uniqueInstance 是否已经被实例化，如果没有被实例化，那么才对实例化语句进行加锁
    public static Singleton3 getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton3.class) {
                // 进行第二次校验
                if (uniqueInstance == null)
                    uniqueInstance = new Singleton3();
            }
        }
        return uniqueInstance;
    }
}
```

### 静态内部类

当 Singleton 类被加载时，静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载，此时初始化 INSTANCE 实例，并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处，而且由 JVM 提供了对线程安全的支持。

```java
public class Singleton4 {

    private Singleton4() {}

    private static class SingletonHolder {
        private static final Singleton4 INSTANCE = new Singleton4();
    }

    public static Singleton4 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
```

## 简单工厂

在创建一个对象时不向客户暴露内部细节，并提供一个创建对象的通用接口。

简单工厂把实例化的操作单独放到一个类中，这个类就成为简单工厂类，让简单工厂类来决定应该用哪个具体子类来实例化。

这样做能把客户类和具体子类的实现解耦，客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个，如果不使用简单工厂，那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变，例如增加子类，那么所有的客户类都要进行修改。

![img](https://github.com/Clloud/Notes/tree/1c4fe8a4ddffd9c16369c1ed4648c39c76385d5a/images/QqHLYiODDtkmZL1N.png)

```java
// pizza接口
public interface Pizza {
    void prepare();
    void bake();
    void cut();
    void box();
}
```

```java
// 各种Pizza实现类，以CheesePizza为例
public class CheesePizza implements Pizza {
    public void prepare() { System.out.println("Preparing cheese pizza...") }
    public void bake() { System.out.println("Baking cheese pizza...") }
    public void cut() { System.out.println("Cutting cheese pizza...") }
    public void box() { System.out.println("Boxing cheese pizza...") }
}
```

```java
// 简单pizza工厂，帮助客户创建pizza
public class SimplePizzaFactory {
    // 所有的客户都使用这个方法来实例化新对象
    public Pizza createPizza(String type) {
        Pizza pizza = null;

        // 创建不同类型的pizza
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();  
        }
        return null;
    }
}
```

```java
// 客户类
public class PizzaStore {
    // 保存一个对SimplePizzaFactory的引用
    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;

        // 通过传入订单类型来使用工厂创建pizza
        pizza = factory.createPizza(type);

        return pizza;
    }
}
```

## 工厂

定义了一个创建对象的接口，但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。

在简单工厂中，是由**一个对象**负责所有具体类的实例化；而在工厂中，是由**一群子类**来负责实例化。

![img](https://github.com/Clloud/Notes/tree/1c4fe8a4ddffd9c16369c1ed4648c39c76385d5a/images/M84nA0mE0n5klJIh.png)

![img](https://github.com/Clloud/Notes/tree/1c4fe8a4ddffd9c16369c1ed4648c39c76385d5a/images/83vVXLK8YMqokxJi.png)

![img](https://github.com/Clloud/Notes/tree/1c4fe8a4ddffd9c16369c1ed4648c39c76385d5a/images/83vVXLK8YMqokx13.png)

```java
// 抽象pizza工厂
public abstract class PizzaStore {
    public void orderPizza(String type) {
        Pizza pizza;

        // PizzaStore的子类在createPizza()方法中处理对象的实例化
        pizza = createPizza(type);

        return pizza;
    }

    // 实例化pizza的责任被移到一个方法中，这个方法就如同是一个工厂
    protected abstract Pizza createPizza(String type);
}
```

```java
// 负责创建pizza的具体子类，以NewYorkPizzaStore为例
public class NYPizzaStore extends PizzaStore {
    public Pizza createPizza(String type) {
        Pizza pizza;
        // 创建不同类型的NYStylePizza
        if (type.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
        } else if (type.equals("clam")) {
            pizza = new NYStyleClamPizza();  
        }
        return pizza;
    }
}
```

## 抽象工厂

## 迭代器

## 观察者

EventListener、消息队列常用的订阅-发布模型

## 代理

[扩展阅读：动态代理与静态代理](https://www.jianshu.com/p/dec1d997619d)

由代理对象组合基础对象，控制对基础对象的访问，扩展新的功能。

代理对象 (Proxy) 通过组合的方式持有基础对象 (RealSubject) 的引用，并且实现了Subject接口。从客户端的角度，Proxy代理对象可以作为Subject接口的替代品。

![image-20200504210440286](https://github.com/Clloud/Notes/tree/1c4fe8a4ddffd9c16369c1ed4648c39c76385d5a/images/lEyrmP2UncJPiBsr.png)

### 静态代理

```java
public interface Payment {
    void pay();
}
```

```java
public class RealPayment implements Payment {
    public void pay() {
        System.out.println("Paying...");
    }
}
```

```java
public class Proxy implements Payment {
    private Payment payment;

       // 保存被代理的类的引用
    public Proxy(Payment p) {
        payment = p;
    }

    public void pay() {
        // 扩展功能
        System.out.println("Before payment");
        // 调用被代理的类的方法
        payment.pay();
        System.out.println("After payment")
    }
}
```

### Java动态代理

```java
// 定义代理对象调用方法时希望执行的动作
Object invoke(Object proxy, Method method, Object[] args)

// 构造实现指定接口的代理类的一个新实例，需要提供类加载器、接口和处理器
static Object new ProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
                                InvocationHandler handler)
```

以下程序使用代理对象对二分查找进行跟踪。首先使用1 - 1000 整数的代理填充数组，然后调用Arrays类中的binarySearch方法在数组中查找一个随机整数，最后打印出与之匹配的元素。

```java
public class ProxyTest {
    public static void main(String[] args) {
        Object[] elements = new Object[100];
        for (int i = 0; i < elements.length; i++) {
            Integer value = i + 1;
            InvocationHandler handler = new TraceHandler(value);
            // 创建一个代理类
            Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class }, handler);
            elements[i] = proxy;
        }

        // 生成一个随机数
        Integer key = new Random().nextInt(elements.length) + 1;

        // 在数组中寻找该随机数
        int result = Arrays.binarySearch(elements, key);

        System.out.println(result);
    }
}

// 动态代理类，用于跟踪方法的执行流程
class TraceHandler implements InvocationHandler {
    private Object target;

    public TraceHandler(Object t) {
        target = t;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
        System.out.print(target);
        // 打印方法名
        System.out.print("." + m.getName() + "(");
        // 打印方法参数
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                System.out.print(args[i]);
                if (i < args.length - 1) System.out.print(", ");
            }
        }
        System.out.println(")");
        // 执行方法
        return m.invoke(target, args);
    }
}
```

运行结果：

```
50.compareTo(77)
75.compareTo(77)
88.compareTo(77)
81.compareTo(77)
78.compareTo(77)
76.compareTo(77)
77.compareTo(77)
76
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://clloud.gitbook.io/notes/she-ji-mo-shi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
