乐趣区

设计模式之禅读书笔记22个设计模式

最近在读秦小波的《设计模式之禅》。本文是一篇长达 2000 行的又水又长的笔记,记录书中所讲 23 个设计模式中的 22 个,基本上是将书中讲的各个设计模式的定义、优点、缺点、适用场景、demo 抄下来了。推荐去阅读原书,这是一位学识丰富且有一个有趣的灵魂的作者所写,原书中每个设计模式的讲解都有一个十分有趣的例子,借助这些例子可以很好的加深理解和记忆。
设计模式感觉都是相通的,之前看过一本《JavaScript 设计模式与开发实践》,其实都是讲的一回事,只是因为语言特性原因在实现方面有所区别,这本书的作者也有一个有趣的灵魂,我现在都还记得代理模式中“小明追美眉”的故事,也推荐阅读。

01 单例模式

定义:

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例

示例:

public class Singleton {private static Singleton singleton = new Singleton();

    // 私有化构造
    private Singleton() {}

    // 获取实例
    public static Singleton getInstance() {return singleton;}

    // 类中其他方法尽量是 static 的
    public static void otherMethod() {}
}

优点:

  1. 单例模式只生成一个实例,所以减少了系统的性能开销
  2. 单例模式可以避免对资源的多重占用
  3. 在内存中,避免对同一资源占用

缺点:

  1. 一般没有接口,扩展困难
  2. 对测试不利
  3. 与单一职责原则违背

适用场景:

  1. 要求生成唯一序列号环境
  2. 创建一个对象需要消耗的资源过多
  3. 需要定义大量的静态常量和静态方法(如工具类)的环境

02 工厂方法模式

定义:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类

示例:

/**
 * 抽象产品
 */
public abstract class Product {public void method1() {System.out.println("method1");
    }
    abstract public void method2();}

/**
 * 具体的一个产品
 */
public class ProductA extends Product {public void method2() {System.out.println("productA method2");
    }
}

/**
 * 具体的一个产品
 */
public class ProductB extends Product {public void method2() {System.out.println("productB method2");
    }
}

/**
 * 抽象工厂
 */
public abstract class Factory {public abstract <T extends Product> T createProduct(Class<T> clazz);
}

/**
 * 具体工厂
 */
public class ProductFactory1 extends Factory {public <T extends Product> T createProduct(Class<T> clazz) {
        Product product = null;
        try {product = (Product) Class.forName(clazz.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();
        } catch (IllegalAccessException e) {e.printStackTrace();
        } catch (ClassNotFoundException e) {e.printStackTrace();
        }
        return (T) product;
    }
}

/**
 * 测试类
 */
public class FactoryTest {
    @Test
    public void test() {Factory factory = new ProductFactory1();
        ProductA productA = factory.createProduct(ProductA.class);
        ProductB productB = factory.createProduct(ProductB.class);
        productA.method2();
        productB.method2();}
}

优点:

  1. 良好的封装性,代码结构清晰
  2. 工厂方法模式的扩展性非常优秀,可以很方便的增加产品类
  3. 屏蔽产品类。只需要关心产品的接口,无需关心实现
  4. 工厂方法模式是典型的解耦框架

使用场景:

  1. 工厂方法模式是 new 一个对象的替代品,但是需要考虑是否需要使用工厂增加代码复杂度
  2. 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式
  3. 工厂方法模式可以用在异构项目中
  4. 可以使用在测试驱动开发的框架下

工厂模式扩展:

  1. 使用静态工厂
/**
 * 静态工厂
 */
public class ProductFactory2 {private ProductFactory2() { }

    static Product createProduct(Class<Product> clazz) {
        Product product = null;
        try {product = (Product) Class.forName(clazz.getName()).newInstance();} catch (ClassNotFoundException e) {e.printStackTrace();
        } catch (IllegalAccessException e) {e.printStackTrace();
        } catch (InstantiationException e) {e.printStackTrace();
        }
        return product;
    }
}
  1. 多工厂模式
  2. 实现单例模式
  3. 延迟初始化

03 抽象工厂模式

定义:

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类

示例:

public class Factory {public ProductA createProductA() {return new ProductA();
    }

    public ProductB createProductB() {return new ProductB();
    }
}

优点:

  1. 封装性,只要知道工厂类是谁就能创建出对象
  2. 产品族内的约束为非公开状态。具体是用哪个产品类创建的是非公开的

缺点:

  1. 难以加产品,没加一个产品都需要改工厂

适用场景:

一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。就是一堆类有相同的父类

04 模板方法模式

定义:

定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

示例:

/**
 * 模板类
 */
public abstract class TemplateClass {protected abstract void method1();
    protected abstract void method2();

    /**
     * 模板方法
     */
    public void templateMethod() {method1();
        method2();}
}

/**
 * 根据模板类创建的类
 */
public class TemplateSubClass1 extends TemplateClass {protected void method1() {System.out.println("1 method1");
    }

    protected void method2() {System.out.println("1 method2");
    }
}

/**
 * 根据模板类创建的类
 */
public class TemplateSubClass2 extends TemplateClass {protected void method1() {System.out.println("2 method1");
    }

    protected void method2() {System.out.println("2 method2");
    }
}

/**
 * 测试类
 */
public class TemplateTest {
    @Test
    public void test() {TemplateSubClass1 templateSubClass1 = new TemplateSubClass1();
        templateSubClass1.templateMethod();
        TemplateSubClass2 templateSubClass2 = new TemplateSubClass2();
        templateSubClass2.templateMethod();}
}

优点:

  1. 封装不变部分,扩展可变部分
  2. 提取公共部分代码,便于维护
  3. 行为由父类控制,子类实现

缺点:

子类对父类产生了影响,在复杂的项目中,会带来代码阅读的难度,会让新手难以适应

适用场景:

多个子类有公有的方法,并且逻辑基本相同

模板方法模式扩展:

可以通过定义开关的形式,调整模板方法的执行流程

05 建造者模式

定义:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

示例:

public class Product {
    private String name;
    private int price;

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public int getPrice() {return price;}

    public void setPrice(int price) {this.price = price;}
}

public class ProductBuilder {private Product product = new Product();

    public void buildName(String name) {product.setName(name);
    }

    public void buildPrice(Integer price) {product.setPrice(price);
    }

    public Product create() {return product;}
}

优点:

  1. 使用建造者模式可以使客户端不必知道产品内部组成的细节
  2. 建造者独立,容易扩展
  3. 由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响

缺点:

  1. 如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
  2. 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大

适用场景:

  1. 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式
  2. 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能
  3. 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时

06 代理模式

定义:

为其他对象提供一种代理以控制对这个对象的访问

示例:

/**
 * 被代理类接口
 */
public interface Subject {void doSomething();
}

/**
 * 真实被代理类
 */
public class RealSubject implements Subject {public void doSomething() {System.out.println("RealSubject doSomething");
    }
}

/**
 * 代理类
 */
public class SubjectProxy implements Subject {
    private Subject subject = null;

    public SubjectProxy() {this.subject = new RealSubject();
    }

    public void doSomething() {System.out.println("doSomething 被代理了");
        this.subject.doSomething();}
}

/**
 * 测试类
 */
public class SubjectProxyTest {
    @Test
    public void test() {SubjectProxy subjectProxy = new SubjectProxy();
        subjectProxy.doSomething();}
}

优点:

  1. 职责清晰。真实类关注自己的逻辑,脏活累活交给代理类
  2. 高可扩展性。对真实类的变更,不需要动代理类

适用场景:

  1. 需要无侵入的对真实类做一些扩展,增强
  2. 期望控制对真实类的访问

代理模式扩展:

  1. 普通代理

就是示例那个,调用方知道代理的存在(类似正向代理)

  1. 透明代理

调用方不知道代理的存在(类似反向代理)

/**
 * 真实被代理类
 */
public class RealSubject implements Subject {public RealSubject(Subject subject) throws ClassNotFoundException {
        // 注意这里
        if (subject == null) {throw new ClassNotFoundException();
        }
    }

    public void doSomething() {System.out.println("RealSubject doSomething");
    }
}

/**
 * 代理类
 */
public class SubjectProxy implements Subject {
    private Subject subject = null;

    public SubjectProxy() {
        // 注意这里
        try {this.subject = new RealSubject(this);
        } catch (ClassNotFoundException e) {e.printStackTrace();
        }
    }

    public void doSomething() {System.out.println("doSomething 被代理了");
        this.subject.doSomething();}
}

/***
 * 测试类
 */
public class ProxyTest {
    @Test
    public void test() {SubjectProxy subjectProxy = new SubjectProxy();
        subjectProxy.doSomething();}

    @Test
    public void test2() throws ClassNotFoundException {
        // 会报错的
//        RealSubject realSubject = new RealSubject();
//        realSubject.doSomething();}
}
  1. 强制代理

必须通过真实角色查找到代理角色,否则你不能访问

感觉意义不是太大

  1. 动态代理

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象

/**
 * 通知接口
 */
public interface Advice {public void exec();
}

/**
 * 通知类
 */
public class BeforeAdvice implements Advice {public void exec() {System.out.println("前置通知执行了");
    }
}

/**
 * 动态代理的 handler
 */
public class MyInvocationHandler implements InvocationHandler {
    // 被代理对象
    private Object target;

    public MyInvocationHandler(Object target) {this.target = target;}

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 寻找 JoinPoint 连结点
        if (true) {new BeforeAdvice().exec();}
        // 执行被代理的方法
        return method.invoke(this.target, args);
    }
}

/**
 * 动态代理类
 */
public class SubjectDynamicProxy {public static Subject newInstance(Subject subject) {ClassLoader classLoader = subject.getClass().getClassLoader();
        Class<?>[] interfaces = subject.getClass().getInterfaces();
        InvocationHandler invocationHandler = new MyInvocationHandler(subject);
        return (Subject) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

/**
 * 测试类
 */
public class ProxyTest {
    @Test
    public void test() {Subject subject = new RealSubject();
        Subject proxySubject = SubjectDynamicProxy.newInstance(subject);
        proxySubject.doSomething();}
}

使用 jdk 的动态代理要求被代理类必须实现一个接口,CGLIB 动态代理没有这个要求。

spring 的 aop 就是使用的动态代理技术实现的

07 原型模式

定义:

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。即基于对象创建对象,而不是类

示例:

public class Prototype implements Cloneable {
    private String name;

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    @Override
    protected Prototype clone() throws CloneNotSupportedException {
        // 关键就是这个东西
        return (Prototype) super.clone();}
}

优点:

  1. 性能优良:基于二进制流拷贝,比 new 性能高
  2. 逃避构造函数的约束

适用场景:

  1. 资源优化场景。类的初始化需要消耗很多资源
  2. 一个对象多个修改者的场景

注意点:

  1. 深拷贝与浅拷贝
  2. clone 与 final 不能并存
  3. 拷贝时构造方法不会执行

08 中介者模式

定义:

用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互

示例:

/**
 * 同事抽象类
 */
public abstract class Colleagure {
    protected Mediator mediator;

    public Colleagure(Mediator mediator) {this.mediator = mediator;}
}

/**
 * 具体同事类
 */
public class ConcreteColleagure1 extends Colleagure {public ConcreteColleagure1(Mediator mediator) {super(mediator);
    }

    public void selfMethod1() {System.out.println("ConcreteColleagure1 自己的业务逻辑");
    }

    public void depMethod1() {
        // 依赖其他对象的逻辑,给中介者处理
        super.mediator.doSomething1();}
}

/**
 * 具体同事类
 */
public class ConcreteColleagure2 extends Colleagure {public ConcreteColleagure2(Mediator mediator) {super(mediator);
    }

    public void selfMethod1() {System.out.println("ConcreteColleagure2 自己的业务逻辑");
    }

    public void depMethod1() {
        // 依赖其他对象的逻辑,给中介者处理
        super.mediator.doSomething2();}
}

/**
 * 抽象中介者
 */
public abstract class Mediator {
    protected ConcreteColleagure1 concreteColleagure1;
    protected ConcreteColleagure2 concreteColleagure2;

    public abstract void doSomething1();
    public abstract void doSomething2();

    public ConcreteColleagure1 getConcreteColleagure1() {return concreteColleagure1;}

    public void setConcreteColleagure1(ConcreteColleagure1 concreteColleagure1) {this.concreteColleagure1 = concreteColleagure1;}

    public ConcreteColleagure2 getConcreteColleagure2() {return concreteColleagure2;}

    public void setConcreteColleagure2(ConcreteColleagure2 concreteColleagure2) {this.concreteColleagure2 = concreteColleagure2;}
}

/**
 * 具体中介者
 */
public class ConcreteMediator extends Mediator {public void doSomething1() {super.concreteColleagure1.selfMethod1();
        super.concreteColleagure2.selfMethod1();}

    public void doSomething2() {super.concreteColleagure2.selfMethod1();
        super.concreteColleagure1.selfMethod1();}
}

/**
 * 测试类
 */
public class MediatorTest {
    @Test
    public void test() {Mediator mediator = new ConcreteMediator();
        ConcreteColleagure2 concreteColleagure2 = new ConcreteColleagure2(mediator);
        ConcreteColleagure1 concreteColleagure1 = new ConcreteColleagure1(mediator);
        mediator.setConcreteColleagure1(concreteColleagure1);
        mediator.setConcreteColleagure2(concreteColleagure2);
        concreteColleagure1.depMethod1();
        concreteColleagure2.depMethod1();}
}

优点:

中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,实现了解耦

适用场景:

  1. 机场调度中心
  2. MVC 框架
  3. 媒体网关
  4. 中介服务

09 命令模式

定义:

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能

示例:

/**
 * 抽象接收者
 */
public abstract class Recevier {public abstract void doSomething();
}

/**
 * 具体的接收者
 */
public class ConcreteRecevier1 extends Recevier {public void doSomething() {System.out.println("ConcreteRecevier1 业务逻辑");
    }
}

/**
 * 具体的接收者
 */
public class ConcreteRecevier2 extends Recevier {public void doSomething() {System.out.println("ConcreteRecevier2 业务逻辑");
    }
}

/**
 * 抽象命令类
 */
public abstract class Command {public abstract void execute();
}

/**
 * 具体命令类
 */
public class ConcreteCommand1 extends Command {

    Recevier recevier;

    public ConcreteCommand1(Recevier recevier) {this.recevier = recevier;}

    public ConcreteCommand1() {this(new ConcreteRecevier1());
    }

    public void execute() {recevier.doSomething();
    }
}

/**
 * 具体命令类
 */
public class ConcreteCommand2 extends Command {

    Recevier recevier;

    public ConcreteCommand2(Recevier recevier) {this.recevier = recevier;}

    public ConcreteCommand2() {this(new ConcreteRecevier2());
    }

    public void execute() {recevier.doSomething();
    }
}

/**
 * 调用者
 */
public class Invoker {
    private Command command;

    public void setCommand(Command command) {this.command = command;}

    public void action() {this.command.execute();
    }
}

/**
 * 测试类
 */
public class CommandTest {
    @Test
    public void test() {Command command = new ConcreteCommand1();
        Command command1 = new ConcreteCommand2();
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.action();
        invoker.setCommand(command1);
        invoker.action();}
}

优点:

  1. 解耦:调用者和接受者之间没有依赖关系
  2. 命令很容易扩展

缺点:

  • 命令的增加会带来类数量的增加

10 责任链模式

定义:

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止

示例:

/**
 * 处理级别
 */
public enum Level {LEVEL1(1, "级别 1"),
    LEVEL2(2, "级别 2"),
    LEVEL3(3, "级别 3");
    private int code;
    private String msg;

    private Level(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {return code;}

    public String getMsg() {return msg;}
}

/**
 * 请求封装
 */
public class Request {
    Level level;

    public Request(Level level) {this.level = level;}

    public Level getRequestLevel() {return this.level;}
}

/**
 * 响应封装
 */
public class Response {
    private int code;
    private String msg;

    public Response(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {return code;}

    public String getMsg() {return msg;}
}

/**
 * 处理请求抽象
 */
public abstract class Handler {
    private Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}

    public final Response handleMessage(Request request) {
        Response response = null;
        if (this.getHandleLevel().getCode() == request.getRequestLevel().getCode()) {
            // 自己处理
            response = this.doRequest(request);
        } else {if (this.nextHandler != null) {
                // 交给下一个人处理
                response = this.nextHandler.handleMessage(request);
            } else {response = new Response(-1, "无人处理");
            }
        }
        return response;
    }

    // 获取当前处理者能够处理的级别
    protected abstract Level getHandleLevel();

    // 处理请求
    protected abstract Response doRequest(Request request);
}

/**
 * 具体处理请求的类
 */
public class Level1Handler extends Handler {protected Level getHandleLevel() {return Level.LEVEL1;}

    protected Response doRequest(Request request) {return new Response(1, "第一个级别在 Level1Handler 处理了");
    }
}

public class Level2Handler extends Handler {protected Level getHandleLevel() {return Level.LEVEL2;}

    protected Response doRequest(Request request) {return new Response(1, "第 2 个级别在 Level2Handler 处理了");
    }
}

public class Level3Handler extends Handler {protected Level getHandleLevel() {return Level.LEVEL3;}

    protected Response doRequest(Request request) {return new Response(1, "第 2 个级别在 Level3Handler 处理了");
    }
}

/**
 * 测试类
 */
public class RequestChainTest {
    @Test
    public void test() {Level1Handler level1Handler = new Level1Handler();
        Level2Handler level2Handler = new Level2Handler();
        Level3Handler level3Handler = new Level3Handler();
        level1Handler.setNextHandler(level2Handler);
        level2Handler.setNextHandler(level3Handler);

        Response response = level1Handler.handleMessage(new Request(Level.LEVEL2));
        System.out.println(response.getCode());
        System.out.println(response.getMsg());
    }
}

优点:

  1. 将请求和处理分开:请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌

11 装饰者模式

定义:

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活

示例:

/**
 * 被装饰类的抽象
 */
public abstract class Component {public abstract void doSomething();
}

/**
 * 具体被装饰者
 */
public class ConcreteComponent extends Component {public void doSomething() {System.out.println("被装饰类的方法");
    }
}


/**
 * 装饰类
 */
public class Decorator extends Component {
    private Component component;

    public Decorator(Component component) {this.component = component;}

    @Override
    public void doSomething() {this.component.doSomething();
    }

    public void newMethod() {System.out.println("新增一个方法");
    }
}

优点:

  1. 装饰类和被装饰类可以独立发展,而不会相互耦合
  2. 装饰模式是继承关系的一个替代方案
  3. 装饰模式可以动态地扩展一个实现类的功能

缺点:

多层的装饰是比较复杂的

适用场景:

  1. 需要扩展一个类

12 策略模式

定义:

定义一组算法,将每个算法都封装起来,并且使它们之间可以互换

示例:

/**
 * 策略接口
 */
public interface Strategy {void doSomething();
}

/**
 * 具体的策略
 */
public class Strategy1 implements Strategy {public void doSomething() {System.out.println("第一种策略");
    }
}

/**
 * 具体的策略
 */
public class Strategy2 implements Strategy {public void doSomething() {System.out.println("第一种策略");
    }
}

/**
 * 封装策略
 */
public class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {this.strategy = strategy;}

    public void execute() {this.strategy.doSomething();
    }
}

/**
 * 测试类
 */
public class StrategyTest {
    @Test
    public void test() {Context context = new Context();
        context.setStrategy(new Strategy1());
        context.execute();}
}

优点:

  1. 算法可以自由切换
  2. 避免使用多重条件判断
  3. 扩展性良好,可以很方便的增加一个策略

缺点:

  1. 策略类数量增多
  2. 所有的策略类都需要对外暴露

适用场景:

  1. 算法可以自由切换场景
  2. 需要屏蔽算法规则的场景

13 适配器模式

定义:

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

示例:

/**
 * 目标角色
 */
public interface Target {void request();
}

/**
 * 源角色
 */
public class Adaptee {public void doAnyThing() {System.out.println("doAnyThing");
    }
}

/**
 * 适配器角色
 */
public class Atapter extends Adaptee implements Target {public void request() {super.doAnyThing();
    }
}

优点:

  1. 适配器模式可以让两个没有任何关系的类在一起运行
  2. 加了类的透明性
  3. 提高了类的复用度
  4. 灵活性非常好

适用场景:

当要改一个已经在生产中适用的类时可以考虑

14 迭代器模式

定义:

它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节

示例:

很多集合类都实现了 Iterable 接口,就是迭代器模式

优点:

遍历方便,隐藏内部细节

15 组合模式

定义:

将对象组合成树形结构以表示“部分 - 整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性

示例:

/**
 * 抽象构件
 */
public abstract class Component {public void doSomething() {System.out.println("一些业务逻辑");
    }
}

/**
 * 树枝
 */
public class Composite extends Component {
    /**
     * 子节点容器
     */
    private ArrayList<Component> componentArrayList = new ArrayList<Component>();

    // 加节点
    public void add(Component component) {this.componentArrayList.add(component);
    }

    // 删节点
    public void remove(Component component) {this.componentArrayList.remove(component);
    }

    // 获取子节点
    public ArrayList<Component> getChildren() {return this.componentArrayList;}
}

public class Leaf extends Component {
    @Override
    public void doSomething() {System.out.println("叶子");
        super.doSomething();}
}

/**
 * 测试类
 */
public class CompositeTest {
    @Test
    public void test() {Composite root = new Composite();
        root.doSomething();

        Composite branch = new Composite();
        Leaf leaf = new Leaf();
        root.add(branch);
        branch.add(leaf);
        display(root);
    }

    private void display(Composite root) {for (Component component1 : root.getChildren()) {if (component1 instanceof Leaf) {component1.doSomething();
            } else {display((Composite) component1);
            }
        }
    }
}

优点:

  1. 高层模块调用简单
  2. 节点自由增加

适用场景:

  • 维护和展示部分 - 整体关系的场景
  • 从一个整体中能够独立出部分模块或功能的场景

16 观察者模式

定义:

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新

示例:

/**
 * 观察者接口
 */
public interface Observer {
    // 更新
    void update(Object object);
}

/**
 * 具体的观察者
 */
public class ConcreteObserver implements Observer {public void update(Object object) {System.out.println("收到了通知"+object.toString());
    }
}

/**
 * 被观察者抽象类
 */
public abstract class Subject {private ArrayList<Observer> observers = new ArrayList<Observer>();

    public void addObserver(Observer observer) {this.observers.add(observer);
    }

    public void removeObserver(Observer observer) {this.observers.remove(observer);
    }

    public void notifyObservers(Object object) {for (Observer observer : this.observers) {observer.update(object);
        }
    }
}

/**
 * 具体的被观察者
 */
public class ConcreteSubject extends Subject {public void doSomething() {super.notifyObservers("doSomething");
    }
}

/**
 * 测试类
 */
public class ObserverTest {
    @Test
    public void test() throws InterruptedException {Observer observer = new ConcreteObserver();
        ConcreteSubject subject = new ConcreteSubject();
        subject.addObserver(observer);
        Thread.sleep(1000);
        subject.doSomething();}
}

优点:

  1. 观察者和被观察者之间是抽象耦合
  2. 建立一套触发机制

缺点:

  1. 开发效率问题
  2. 运行效率问题

适用场景:

  1. 跨系统的消息交换场景
  2. 关联行为场景

17 门面模式

定义:

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用

示例:

/**
 * 子系统
 */
public class SubSystemA {public void doSomethingA() {System.out.println("doSomethingA");
    }
}

/**
 * 子系统
 */
public class SubSystemB {public void doSomethingB() {System.out.println("doSomethingB");
    }
}


/**
 * 子系统
 */
public class SubSystemC {public void doSomethingC() {System.out.println("doSomethingC");
    }
}

/**
 * 门面
 */
public class Facade {private SubSystemA subSystemA = new SubSystemA();
    private SubSystemB subSystemB = new SubSystemB();
    private SubSystemC subSystemC = new SubSystemC();

    public void methodA() {subSystemA.doSomethingA();
    }

    public void methodB() {subSystemB.doSomethingB();
    }

    public void methodC() {subSystemC.doSomethingC();
    }
}

优点:

  1. 减少系统的相互依赖
  2. 提高了灵活性
  3. 提高了安全性

缺点:

  1. 不符合开闭原则

适用场景:

  1. 为一个复杂的模块或子系统提供一个供外界访问的接口
  2. 子系统相对独立
  3. 预防低水平人员带来的风险扩散

18 备忘录模式

定义:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

示例:

/**
 * 发起人角色
 */
public class Originator {
    private String state;   // 内部状态

    public Originator() {}

    public Originator(String state) {this.state = state;}

    public String getState() {return state;}

    public void setState(String state) {this.state = state;}

    // 创建备忘录
    public Memento createMemento() {return new Memento(this.state);
    }

    // 恢复备忘录
    public void restoreMemento(Memento memento) {this.setState(memento.getState());
    }

    public void changeState(String state) {this.setState(state);
    }
}

/**
 * 备忘录
 */
public class Memento {
    private String state; // 发起人内部状态

    public Memento(String state) {this.state = state;}

    public String getState() {return state;}

    public void setState(String state) {this.state = state;}
}

/**
 * 备忘录管理员角色
 */
public class Caretaker {
    private Memento memento;

    public Memento getMemento() {return memento;}

    public void setMemento(Memento memento) {this.memento = memento;}
}

/**
 * 测试类
 */
public class MementoTest {
    @Test
    public void test() {Originator originator = new Originator("1");
        System.out.println(originator.getState());
        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(originator.createMemento());
        originator.changeState("3");
        System.out.println(originator.getState());
        originator.restoreMemento(caretaker.getMemento());
        System.out.println(originator.getState());
    }
}

适用场景:

  1. 需要保存和恢复数据的相关状态场景

19 访问者模式

定义:

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作

示例:

/**
 * 访问者接口
 */
public interface IVisitor {void visit(ConcreteElement1 concreteElement1);
    void visit(ConcreteElement2 concreteElement2);
}

/**
 * 具体的访问者
 */
public class Visitor implements IVisitor {public void visit(ConcreteElement1 concreteElement1) {concreteElement1.doSomething();
    }

    public void visit(ConcreteElement2 concreteElement2) {concreteElement2.doSomething();
    }
}

/**
 * 抽象元素
 */
public abstract class Element {public abstract void doSomething(); // 业务逻辑

    public abstract void accept(IVisitor visitor); // 允许谁访问
}

/**
 * 具体元素
 */
public class ConcreteElement1 extends Element {public void doSomething() {System.out.println("element1 业务逻辑");
    }

    public void accept(IVisitor visitor) {visitor.visit(this);
    }
}

/**
 * 具体元素
 */
public class ConcreteElement2 extends Element {public void doSomething() {System.out.println("element2 业务逻辑");
    }

    public void accept(IVisitor visitor) {visitor.visit(this);
    }
}

/**
 * 结构对象
 */
public class ObjectStructure {public static Element createElement() {Random random = new Random();
        if(random.nextInt(100) < 50) {return new ConcreteElement1();
        } else {return new ConcreteElement2();
        }
    }
}

/**
 * 测试类
 */
public class VisitorTest {
    @Test
    public void test() {for (int i = 0; i < 10; i++) {Element e = ObjectStructure.createElement();
            e.accept(new Visitor());
        }
    }
}

优点:

  1. 符合单一职责原则
  2. 可扩展性高
  3. 灵活性高

缺点:

  1. 具体元素变更比较困难
  2. 具体元素对访问者公布细节
  3. 违背了依赖倒置转原则

适用场景:

业务规则要求遍历多个不同的对象

20 状态模式

定义:

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类

示例:

/**
 * 状态抽象类
 */
public abstract class State {
    protected Context context;

    public void setContext(Context context) {this.context = context;}

    // 行为 1
    public abstract void handle1();
    // 行为 2
    public abstract void hanle2();}

/**
 * 具体状态
 */
public class State1 extends State {public void handle1() { }

    public void hanle2() {super.context.setCurrentState(Context.STATE2);
        System.out.println("在状态 1 可以执行 handle2");
    }
}

/**
 * 具体状态
 */
public class State2 extends State {public void handle1() {super.context.setCurrentState(Context.STATE1);
        System.out.println("在状态 2 可以执行 handle1");
    }

    public void hanle2() {}
}

/**
 * 环境角色
 */
public class Context {public static final State STATE1 = new State1();
    public static final State STATE2 = new State2();

    private State currentState;

    public State getCurrentState() {return currentState;}

    public void setCurrentState(State state) {
        this.currentState = state;
        this.currentState.setContext(this);
    }

    public void handle1() {this.currentState.handle1();
    }

    public void handle2() {this.currentState.hanle2();
    }
}

/**
 * 测试类
 */
public class StateTest {
    @Test
    public void test() {Context context = new Context();
        context.setCurrentState(new State1());
        context.handle1();
        context.handle2();
        context.handle2();
        context.handle1();}
}

优点:

  1. 结构清晰
  2. 封装性好
  3. 遵循设计原则

缺点:

  1. 类数量比较多

适用场景:

  1. 行为随状态改变而改变的场景
  2. 条件、分支判断语句的替代者

21 享元模式

定义:

使用共享对象可有效地支持大量的细粒度的对象

示例:

/**
 * 抽象享元角色
 */
public abstract class FlyWeight {
    private String intrinsic;
    protected final String extrinsic;

    public FlyWeight(String extrinsic) {this.extrinsic = extrinsic;}

    public abstract void operate(); // 业务逻辑

    public String getIntrinsic() {return intrinsic;}

    public void setIntrinsic(String intrinsic) {this.intrinsic = intrinsic;}
}

/**
 * 具体享元角色
 */
public class ConcreteFlyWeight extends FlyWeight {public ConcreteFlyWeight(String extrinsic) {super(extrinsic);
    }

    public void operate() {System.out.println("业务逻辑");
    }
}

/**
 * 享元对象工厂
 */
public class FlyWeightFactory {private static HashMap<String, FlyWeight> flyWeightHashMap = new HashMap<String, FlyWeight>();

    public static FlyWeight getFlyWeight(String extrinsic) {
        FlyWeight flyWeight = null;
        if (flyWeightHashMap.containsKey(extrinsic)) {flyWeight = flyWeightHashMap.get(extrinsic);
        } else {flyWeight = new ConcreteFlyWeight(extrinsic);
            flyWeightHashMap.put(extrinsic, flyWeight);
        }
        return flyWeight;
    }
}

优点:

可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能

缺点:

提高了系统复杂性,需要分离出外部状态和内部状态

适用场景:

  • 系统中存在大量相似的对象
  • 需要缓冲池的场景

22 桥梁模式

定义:

将抽象和实现解耦,使得两者可以独立地变化

示例:

/**
 * 实现化角色
 */
public interface Implementor {void doSomething();
    void doAnyThing();}

/**
 * 具体实现化角色
 */
public class ConcreteImplementor implements Implementor{public void doSomething() {System.out.println("doSomething");
    }

    public void doAnyThing() {System.out.println("doAnything");
    }
}

/**
 * 抽象化角色
 */
public abstract class Abstraction {
    private Implementor implementor;

    public Abstraction(Implementor implementor) {this.implementor = implementor;}

    public void request() {this.implementor.doSomething();
    }

    public Implementor getImplementor() {return implementor;}
}

/**
 * 具体的抽象化角色
 */
public class ConcreteAbstraction extends Abstraction {public ConcreteAbstraction(Implementor implementor) {super(implementor);
    }

    @Override
    public void request() {super.request();
        super.getImplementor().doAnyThing();
    }
}

优点:

  1. 抽象和实现分离
  2. 优秀的扩充能力
  3. 实现细节对客户透明

适用场景:

  1. 不希望或不适用使用继承的场景
  2. 接口或抽象类不稳定的场景
  3. 重用性要求较高的场景

退出移动版