可扩展性的本质是找到系统的变化点,并隔离变化点。

世间众多设计模式其实就是一种设计模式即隔离变化点的模式。

极致扩展性的标志,就是需求的新增,不会在原有代码交付物上进行任何形式的修改。

——《阿里巴巴Java开发手册》


1. 设计原则



1.1 开闭原则

定义

软件实体应当对扩展开放,对修改关闭。

实现

可以通过“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

作用

开闭原则是面向对象程序设计的终极目标,它使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性。具体来说,其作用如下。

  1. 对软件测试的影响

软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。

  1. 可以提高代码的可复用性

粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。

  1. 可以提高软件的可维护性

遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。


1.2 里式替换原则

定义

继承必须确保超类所拥有的性质在子类中仍然成立

实现

子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

根据上述理解,对里氏替换原则的定义可以总结如下:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

作用

  • 里氏替换原则是实现开闭原则的重要方式之一。

  • 它克服了继承中重写父类造成的可复用性变差的缺点。

  • 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。

  • 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。


1.3 依赖倒置原则

定义

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

实现

  • 每个类尽量提供接口或抽象类,或者两者都具备。

  • 变量的声明类型尽量是接口或者是抽象类。

  • 任何类都不应该从具体类派生。

  • 使用继承时尽量遵循里氏替换原则。

作用

  • 依赖倒置原则可以降低类间的耦合性。
  • 依赖倒置原则可以提高系统的稳定性。
  • 依赖倒置原则可以减少并行开发引起的风险。
  • 依赖倒置原则可以提高代码的可读性和可维护性。

1.4 单一职责原则

定义

一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分

实现

单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。

作用

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
  • 提高类的可读性。复杂性降低,自然其可读性会提高。
  • 提高系统的可维护性。可读性提高,那自然更容易维护了。
  • 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。

1.5 接口隔离原则

定义

要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

实现

在具体应用接口隔离原则时,应该根据以下几个规则来衡量。

  • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

作用

接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。

  • 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

  • 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。

  • 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。

  • 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。

  • 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。


1.6 迪米特法则

定义

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。

实现

  • 从依赖者的角度来说,只依赖应该依赖的对象。

  • 从被依赖者的角度说,只暴露应该暴露的方法。

作用

迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。

  • 降低了类之间的耦合度,提高了模块的相对独立性。

  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。


2. 设计模式


2.1 单例模式-Singleton

定义

一个类只有一个实例,且该类能自行创建这个实例

特点

  1. 单例类只有一个实例对象。
  2. 该单例对象必须由单例类自行创建。
  3. 单例类对外提供一个访问该单例的全局访问点。

优点

  • 单例模式可以保证内存中只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

缺点

  • 单例模式一般没有接口,扩展困难,如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

结构

  • 单例类
  • 访问类

实现

  • 懒汉式单例
package top.parak.singleton.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.singeleton.pattern </p>
 * <p> FileName: LazySingleton <p>
 * <p> Description: 懒汉式单例 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/16
 */

/**
 * <p> 特点:
 * 类加载时没有生成单例,只有第一次调用getInstance方法时才会创建这个单例
 * </p>
 */
public class LazySingleton {

    /* volatile: 具有可见性、有序性,不具备原子性
    volatile声明变量的值可能随时会被其他线程修改,
    使用volatile修饰的变量会强制将修改的值立即写入主存,
    主存中值的更新会使缓存中的值失效。
    volatile不会让线程阻塞,响应速度比synchronized快 */

    // volatile保证instance在所有线程中同步可见
    private static volatile LazySingleton instance = null;

    // private避免类在外部被实例化
    private LazySingleton() {}

    // 加锁同步,保证getInstance()只能被一个线程调用
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

}
  • 饿汉式单例
package top.parak.singleton.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.singeleton.pattern </p>
 * <p> FileName: HungrySingleton <p>
 * <p> Description: 饿汉式单例 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/16
 */

/**
 * <p> 特点:
 * 类一旦加载就创建一个单例,保证在调用getInstance()之前单例就已经存在
 * </p>
 */

public class HungrySingleton {

    /* 在类加载时就创建一个静态对象供系统使用,以后不再改变,
    所以是线程安全的,可以直接用于多线程而不会出现问题 */

    private static final HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {}

    public static HungrySingleton getInstance() {
        return instance;
    }
}

2.2 原型模式-Prototype

定义

用一个已经创建的实例,通过复制该原型对象来创建一个和原型相同或者相似的新对象

优点

  • Java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可复制实现撤销操作。

缺点

  • 需要为每一个类都配置一个clone方法。
  • clone方法位于类的内部,当对于已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,浅克隆和深克隆需要需要运用得当。

结构

  • 抽象原型类:规定了具体原型对象必须实现的接口。
  • 具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的clone()方法类复制新的对象。

实现

package top.parak.prototype.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.prototype.pattern </p>
 * <p> FileName: Prototype <p>
 * <p> Description: 原型模式浅克隆 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/16
 */

import org.apache.log4j.Logger;

/**
 * <p> 特点:
 * 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,扔指向原有属性所指向的对象的内存地址
 * 深克隆:创建一个对象,属性中引用的对象也会被克隆,不再指向原有对象地址
 * </p>
 */
public class Prototype {
    public static Logger log = Logger.getLogger(Prototype.class);
    public static void main(String[] args) throws CloneNotSupportedException {
        RealizeType realizeType1 = new RealizeType();
        RealizeType realizeType2 = (RealizeType) realizeType1.clone();
        log.info("realizeType1 == realizeType2 ? " + (realizeType1 == realizeType2));
    }
}

class RealizeType implements Cloneable {

    private Logger log = Logger.getLogger(RealizeType.class);

    RealizeType() {
        log.info("原型创建");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        log.info("原型复制");
        return super.clone();
    }
}

2.3 简单工厂模式-Simple Factory

定义

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。

按照业务场景划分,工厂模式有3种实现方式

image-20201111164349538

三种实现方式

说明

简单工厂模式有一个具体的工厂类,可以生产多个不同的产品,属于创建型设计模式,但是简单工厂模式不在GOF23中设计模式之列。

在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫做静态工厂方法模式(Static Factory Method Pattern)。

优点

  • 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建响应的产品。工厂和产品的职责区分明确。
  • 客户端无需知道所创建具体产品的类名,只需知道参数即可。
  • 也可以引入配置文件,在不修改客户端的代码的情况下更换和添加新的具体产品类。

缺点

  • 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受到影响。且工厂类代码会非常臃肿,违背高聚合原则。
  • 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
  • 系统拓展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。

应用场景

对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

结构

  • 简单工厂(Simple Factory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象产品(Abstract Product):是简单工厂创建的所有对象的父亲,负责描述所有实例共有的公共接口。
  • 具体产品(Concrete Product):是简单工厂模式的创建目标。

实现

package top.parak.simpleFactory.pattern;

import org.apache.log4j.Logger;
import java.util.Scanner;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.simpleactory.pattern </p>
 * <p> FileName: Client <p>
 * <p> Description: 简单工厂模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/17
 */

/**
 * <p>测试类</p>
 */
public class SimpleFactoryDemo {
    private static Logger log = Logger.getLogger(SimpleFactoryDemo.class);
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("输入产品型号:");
            int choice = scanner.nextInt();
            switch (choice) {
                case 0: log.info("成功退出"); return;
                case 1: SimpleFactory.makeProduct(Constant.PRODUCT_1).show(); break;
                case 2: SimpleFactory.makeProduct(Constant.PRODUCT_2).show(); break;
                case 3: SimpleFactory.makeProduct(Constant.PRODUCT_3).show(); break;
                default: log.info("该产品不存在,请重新输入!"); break;
            }
        }
    }
}

/**
 * <p>
 *     抽象产品
 * </p>
 */
interface product {
    void show();
}

/**
 * <p>
 *     枚举所有产品
 * </p>
 */
enum Constant {
    PRODUCT_1,
    PRODUCT_2,
    PRODUCT_3;
}

/**
 * <p>
 *     具体产品1
 * </p>
 */
class ConcreteProduct1 implements product {
    private Logger log = Logger.getLogger(ConcreteProduct1.class);
    public void show() {
        log.info("具体产品1显示...");
    }
}

/**
 * <p>
 *     具体产品2
 * </p>
 */
class ConcreteProduct2 implements product {
    private Logger log = Logger.getLogger(ConcreteProduct2.class);
    public void show() {
        log.info("具体产品2显示...");
    }
}

/**
 * <p>
 *     具体产品3
 * </p>
 */
class ConcreteProduct3 implements product {
    private Logger log = Logger.getLogger(ConcreteProduct3.class);
    public void show() {
        log.info("具体产品3显示...");
    }
}

/**
 * <p>
 *     简单工厂
 * </p>
 */
class SimpleFactory {
    public static product makeProduct(Constant c) {
        switch (c) {
            case PRODUCT_1:
                return new ConcreteProduct1();
            case PRODUCT_2:
                return new ConcreteProduct2();
            case PRODUCT_3:
                return new ConcreteProduct3();
        }
        return null;
    }
}

2.4 工厂方法模式-Factory Method

说明

工厂方法模式是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

工厂方法模式考虑的是一种工厂负责一类产品的生产,畜牧场只养动物、电视机厂只生产电视机、计算机软件学院只培养计算机软件专业的学生等。

优点

  • 用户只需要具体工厂的名称就可得到所要的产品,无需知道产品的具体创建过程。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无需关系其他实现类,满足迪米特法则、依赖倒置原则和里式替换原则。

缺点

  • 类的个数容易过多,增加复杂度。
  • 增加了系统的抽象性和理解难度。
  • 一个工厂只能生产一种产品,此弊端可使用抽象工厂模式解决。

结构

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法类创建产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Abstract Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(Concrete Product):实现了抽象产品角色定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

实现

package top.parak.factoryMethod.pattern;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.factoryMethod.pattern </p>
 * <p> FileName: FactoryMethod <p>
 * <p> Description: 工厂方法模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/17
 */

/**
 * <p>测试类</p>
 */
public class FactoryMethodDemo {
    private static Logger log = Logger.getLogger(FactoryMethodDemo.class);

    /**
     * <p>
     * 读取XML配置文件,提取具体类名,返回实例对象集合
     * </p>
     * @return List<Object>
     * @throws IOException
     * @throws SAXException
     * @throws ParserConfigurationException
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static List<Object> getObjects(String filePath) throws IOException, SAXException, ParserConfigurationException,  ClassNotFoundException, IllegalAccessException, InstantiationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse(new File(filePath));
        NodeList nodeList = document.getElementsByTagName("className");
        List<Object> objects = new LinkedList<>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            String packageName = FactoryMethodDemo.class.getPackage().getName();
            String className = nodeList.item(i).getTextContent();
            Class<?> c = Class.forName(packageName + "." + className);
            objects.add(c.newInstance());
        }
        return objects;
    }

    public static void main(String[] args) {
        try {
            List<Object> objects = getObjects("src/main/java/top/parak/factoryMethod/pattern/config.xml");
            objects.stream().forEach( o -> {
                AbstractFactory factory = (AbstractFactory) o;
                try {
                    factory.produce().show();
                } catch (InterruptedException e) {
                    log.error(e.getMessage());
                };
            });
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

/**
 * <P>
 *     抽象产品
 * </p>
 */
interface product {
    void show();
}

/**
 * <p>
 *     具体产品1
 * </p>
 */
class ConcreteProduct1 implements product {
    private Logger log = Logger.getLogger(ConcreteProduct1.class);
    public void show() {
        log.info("具体产品1展示...");
    }
}

/**
 * <p>
 *     具体产品2
 * </p>
 */
class ConcreteProduct2 implements product {
    private Logger log = Logger.getLogger(ConcreteProduct2.class);
    public void show() {
        log.info("具体产品2展示...");
    }
}

/**
 * <p>
 *     具体产品3
 * </p>
 */
class ConcreteProduct3 implements product {
    private Logger log = Logger.getLogger(ConcreteProduct3.class);
    public void show() {
        log.info("具体产品3展示...");
    }
}

/**
 * <p>
 *     抽象工厂
 * </p>
 */
interface AbstractFactory {
    public product produce() throws InterruptedException;
}

/**
 * <p>
 *     具体工厂1
 * </p>
 */
class ConcreteFactory1 implements AbstractFactory {
    private Logger log = Logger.getLogger(ConcreteFactory1.class);
    public product produce() {
        log.info("具体工厂1生产中...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
        log.info("===>产品1生产完成");
        return new ConcreteProduct1();
    }
}

/**
 * <p>
 *     具体工厂2
 * </p>
 */
class ConcreteFactory2 implements AbstractFactory {
    private Logger log = Logger.getLogger(ConcreteFactory2.class);
    public product produce() {
        log.info("具体工厂2生产中...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
        log.info("===>产品2生产完成");
        return new ConcreteProduct2();
    }
}

/**
 * <p>
 *     具体工厂3
 * </p>
 */
class ConcreteFactory3 implements AbstractFactory {
    private Logger log = Logger.getLogger(ConcreteFactory3.class);
    public product produce() {
        log.info("具体工厂3生产中...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
        log.info("===>产品3生产完成");
        return new ConcreteProduct3();
    }
}

config.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <className>ConcreteFactory1</className>
    <className>ConcreteFactory2</className>
    <className>ConcreteFactory3</className>
</config>

运行结果

2020-10-17 11:25:34 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteFactory1] [thread: main] [FactoryMethod.java: 143] - 具体工厂1生产中...
2020-10-17 11:25:35 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteFactory1] [thread: main] [FactoryMethod.java: 149] - ===>产品1生产完成
2020-10-17 11:25:35 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteProduct1] [thread: main] [FactoryMethod.java: 98] - 具体产品1展示...
2020-10-17 11:25:35 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteFactory2] [thread: main] [FactoryMethod.java: 162] - 具体工厂2生产中...
2020-10-17 11:25:36 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteFactory2] [thread: main] [FactoryMethod.java: 168] - ===>产品2生产完成
2020-10-17 11:25:36 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteProduct2] [thread: main] [FactoryMethod.java: 110] - 具体产品2展示...
2020-10-17 11:25:36 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteFactory3] [thread: main] [FactoryMethod.java: 181] - 具体工厂3生产中...
2020-10-17 11:25:37 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteFactory3] [thread: main] [FactoryMethod.java: 187] - ===>产品3生产完成
2020-10-17 11:25:37 PARAK INFO  [top.parak.factoryMethod.pattern.ConcreteProduct3] [thread: main] [FactoryMethod.java: 122] - 具体产品3展示...

2.5 抽象工厂模式-Abstract Factory

说明

工厂方法模式:一个工厂只负责一类产品的生产,像小作坊,电器厂只生产电风扇。
抽象工厂模式:一个工厂负责多种相关产品的生产,像综合型工厂,电器厂生产电视机、空调和冰箱等。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,抽象工厂模式可生产多个等级的产品。

定义

一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

使用条件

  • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

优点

除了包含工厂方法模式的优点,还有如下优点:

  • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品族。
  • 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

缺点

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

结构

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Abstract Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

实例

package top.parak.abstractFactory.example;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.abstractFactory.example </p>
 * <p> FileName: Farm <p>
 * <p> Description: 抽象工厂模式模拟农场 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/17
 */

import org.apache.log4j.Logger;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * <p>
 *     农场测试类
 * </p>
 */
public class FarmDemo {
    private JFrame frame = new JFrame("FarmDemo");
    private JDesktopPane desktopPane = new JDesktopPane();
    private JPanel contentPane = (JPanel) frame.getContentPane();
    private JPanel leftPanel = new JPanel();
    private final ImageIcon backIcon = new ImageIcon("img/Farm.jpg");
    private final JLabel rightLabel = new JLabel(backIcon);
    private final JLabel tipLabel = new JLabel("选择产品");
    private final JButton produceButton = new JButton("开始生产");
    private final Font font = new Font("微软雅黑", Font.PLAIN, 20);
    private JComboBox<String> productComboBox = new JComboBox<>();
    private JTextArea textArea = new JTextArea();

    private AbstractFarm baoXingFarm = new BaoXingFarm();
    private AbstractFarm zhouQiaoFarm = new ZhouQiaoFarm();

    public FarmDemo() {
        contentPane.setOpaque(false);
        contentPane.setLayout(null);
        rightLabel.setBounds(300, 0, backIcon.getIconWidth(), backIcon.getIconHeight());
        desktopPane.setLayout(null);
        desktopPane.add(rightLabel, new Integer(Integer.MIN_VALUE));
        leftPanel.setBackground(Color.CYAN);
        leftPanel.setBounds(0, 0, 300, 420);
        desktopPane.add(leftPanel);

        leftPanel.setLayout(null);
        tipLabel.setFont(font);
        tipLabel.setBounds(5, 10, 90, 30);
        leftPanel.add(tipLabel);
        String[] products = new String[]{"马", "牛", "青菜", "白菜"};
        for (String product : products) {
            productComboBox.addItem(product);
        }
        productComboBox.setFont(font);
        produceButton.setFont(font);
        productComboBox.setBounds(90, 10, 80, 30);
        leftPanel.add(productComboBox);
        produceButton.setBounds(180, 10, 115, 30);
        leftPanel.add(produceButton);
        produceButton.addActionListener(new ProductAction());

        textArea.setFont(new Font("微软雅黑", Font.PLAIN, 14));
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setBounds(10, 50, 280, 360);
        scrollPane.setBorder(new TitledBorder("生产日志"));
        scrollPane.getViewport().setOpaque(false);
        leftPanel.add(scrollPane);

        frame.setContentPane(desktopPane);
        frame.setBounds(300,300,1500,500);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private int index = 0;
    class ProductAction implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            JInternalFrame internalFrame = null;
            switch (productComboBox.getSelectedItem().toString()) {
                case "马":
                    internalFrame = zhouQiaoFarm.produceAnimalProduct().showImage();
                    textArea.append(getTime() + " 周桥农场:新马出生\n");
                    break;
                case "牛":
                    internalFrame = baoXingFarm.produceAnimalProduct().showImage();
                    textArea.append(getTime() + " 宝兴农场:新牛出生\n");
                    break;
                case "青菜":
                    internalFrame = baoXingFarm.producePlantProduct().showImage();
                    textArea.append(getTime() + " 宝兴农场:青菜出生\n");
                    break;
                case "白菜":
                    internalFrame = zhouQiaoFarm.producePlantProduct().showImage();
                    textArea.append(getTime() + " 周桥农场:白菜出生\n");
                    break;
                default:
                    throw new IllegalArgumentException("Error");
            }
            internalFrame.setBounds(index + 300, 100, 300, 350);
            desktopPane.add(internalFrame);
            index = (index + 300) % 1200;
        }

        private String getTime() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        }
    }

    public static void main(String[] args) {
        new FarmDemo();
    }
}

/**
 * <p>
 *     抽象产品:动物类
 * </p>
 */
interface AnimalProduct {
    public JInternalFrame showImage();
}

/**
 * <p>
 *     抽象产品:植物类
 * </p>
 */
interface PlantProduct {
    public JInternalFrame showImage();
}

/**
 * <p>
 *     动物产品展示窗口
 * </p>
 */
class AnimalFrame extends JInternalFrame implements AnimalProduct {
    public AnimalFrame(String animalName, String imagePath) {
        Container contentPane = getContentPane();
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, 1));
        panel.setBorder(BorderFactory.createTitledBorder("动物:" + animalName));
        JScrollPane scrollPane = new JScrollPane(panel);
        contentPane.add(scrollPane, BorderLayout.CENTER);
        JLabel label = new JLabel(new ImageIcon(imagePath));
        panel.add(label);
        pack();
        setTitle(animalName);
        setVisible(false);
        setClosable(true);
        setIconifiable(true);
        setResizable(false);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    }

    @Override
    public JInternalFrame showImage() {
        this.setVisible(true);
        return this;
    }
}

/**
 * <p>
 *     植物产品展示窗口
 * </p>
 */
class PlantFrame extends JInternalFrame implements PlantProduct{
    public PlantFrame(String plantName, String imagePath) {
        Container contentPane = getContentPane();
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, 1));
        panel.setBorder(BorderFactory.createTitledBorder("植物:" + plantName));
        JScrollPane scrollPane = new JScrollPane(panel);
        contentPane.add(scrollPane, BorderLayout.CENTER);
        JLabel label = new JLabel(new ImageIcon(imagePath));
        panel.add(label);
        pack();
        setTitle(plantName);
        setVisible(false);
        setClosable(true);
        setIconifiable(true);
        setResizable(false);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    }

    @Override
    public JInternalFrame showImage() {
        this.setVisible(true);
        return this;
    }
}

/**
 * <p>
 *     具体动物产品:马类
 * </p>
 */
class Horse extends AnimalFrame  {
    public Horse() {
        super("马-🐴", "img/Horse.jpg");
    }
}

/**
 * <p>
 *     具体动物产品:牛类
 * </p>
 */
class Cattle extends AnimalFrame {
    public Cattle() {
        super("牛-🐮", "img/Cattle.jpg");
    }
}

/**
 * <p>
 *     具体植物产品:青菜
 * </p>
 */
class Vegetable extends PlantFrame {
    public Vegetable() {
        super("青菜", "img/Vegetable.jpg");
    }
}

/**
 * <p>
 *     具体植物产品:白菜
 * </p>
 */
class Cabbage extends PlantFrame {
    public Cabbage() {
        super("白菜", "img/Cabbage.jpg");
    }
}

/**
 * <p>
 *     抽象工厂:农场类
 * </p>
 */
interface AbstractFarm {
    public AnimalProduct produceAnimalProduct();
    public PlantProduct producePlantProduct();
}

/**
 * <p>
 *     具体工厂:宝兴农场
 *     生产产品:牛、青菜
 * </p>
 */
class BaoXingFarm implements AbstractFarm {
    private Logger log = Logger.getLogger(BaoXingFarm.class);
    @Override
    public AnimalProduct produceAnimalProduct() {
        log.info("宝兴农场===>新牛出生");
        return new Cattle();
    }

    @Override
    public PlantProduct producePlantProduct() {
        log.info("宝兴农场===>青菜长成");
        return new Vegetable();
    }
}

/**
 * <p>
 *     具体农场:周桥农场
 *     生产产品:马、白菜
 * </p>
 */
class ZhouQiaoFarm implements AbstractFarm {
    private Logger log = Logger.getLogger(ZhouQiaoFarm.class);
    @Override
    public AnimalProduct produceAnimalProduct() {
        log.info("周桥农场===>新马出生");
        return new Horse();
    }

    @Override
    public PlantProduct producePlantProduct() {
        log.info("周桥农场===>白菜长成");
        return new Cabbage();
    }
}

运行结果

image-20201017185342528


2.6 建造者模式-Builder

定义

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

优点

  • 封装性好,构建和表示分离。
  • 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  • 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其他模块产生任何影响,便于控制细节风险。

缺点

  • 产品的组成部分必须相同,这限制了其使用范围。
  • 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

结构

  • 产品角色(Product):包含多个组件的复杂对象,由具体建造者来创建其各个零部件。

  • 抽象建造者(Abstract Builder):包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法getResult()。

  • 具体建造者(Concrete Builder):实现抽象建造者的接口,完成复杂产品的各个部件的具体创建方法。

  • 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

实现

package top.parak.builder.pattern;

import lombok.Data;
import org.apache.log4j.Logger;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.builder.pattern </p>
 * <p> FileName: Builder <p>
 * <p> Description: 建造者模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/17
 */

/**
 * <p>测试类</p>
 */
public class BuilderDemo {
    private static Logger log = Logger.getLogger(BuilderDemo.class);
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();
        Director director = new Director(builder);
        Product product = director.construct();
        log.info(product.toString());
    }
}

/**
 * <p>
 *     抽象产品类
 * </p>
 */
@Data
class Product {
    private String componentA;
    private String componentB;
    private String componentC;
}

/**
 * <p>
 *     抽象建造者
 * </p>
 */
abstract class Builder {
    protected Product product = new Product();
    public abstract void buildComponentA();
    public abstract void buildComponentB();
    public abstract void buildComponentC();
    public Product getProduct() {
        return product;
    }
}

/**
 * <p>
 *     具体建造者
 * </p>
 */
class ConcreteBuilder extends Builder {

    @Override
    public void buildComponentA() {
        product.setComponentA("建造零件A");
    }

    @Override
    public void buildComponentB() {
        product.setComponentB("建造零件B");
    }

    @Override
    public void buildComponentC() {
        product.setComponentC("建造零件C");
    }
}

/**
 * <P>
 *     指挥者
 * </p>
 */
class Director {
    private Builder builder;
    public Director(Builder builder) {
        this.builder = builder;
    }
    public Product construct() {
        builder.buildComponentA();
        builder.buildComponentB();
        builder.buildComponentC();
        return builder.getProduct();
    }
}

2.7 代理模式-Proxy

定义

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。

优点

  • 代理模式在客户端和目标对象之间起到一个中介作用和保护目标对象的作用。
  • 代理对象可以扩展对象的功能。
  • 代理模式能将客户端与目标对象分离,在一定程度上降低系统的耦合度,增加了系统的可扩展性。

缺点

  • 代理模式会造成系统设计中类的数量增加。
  • 代理模式能将客户端与目标对象之间增加一个代理对象,会造成请求处理速度变慢。
  • 增加了系统的复杂度。

结构

  • 抽象主题(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实主题(Real Subject):实现了抽象主题中的具体业务,是代理对象锁代表的真实对象,是最终要引用的对象。
  • 代理(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
package top.parak.proxy.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.proxy.pattern </p>
 * <p> FileName: Proxy <p>
 * <p> Description: 代理模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/18
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class ProxyDemo {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

/**
 * <p>
 *     抽象主题
 * </p>
 */
interface Subject {
    void request();
}

/**
 * <p>
 *     真实主题
 * </p>
 */
class RealSubject implements Subject {
    private Logger log = Logger.getLogger(RealSubject.class);
    @Override
    public void request() {
        log.info("访问真实主题...");
    }
}

/**
 * <p>
 *     代理
 * </p>
 */
class Proxy implements Subject {
    private Logger log = Logger.getLogger(Proxy.class);
    private RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
            preHandler();
            realSubject.request();
            postHandler();
        }
    }

    public void preHandler() {
        log.info("访问真实主题之前的预处理");
    }

    public void postHandler() {
        log.info("访问真实主题之后的后续处理");
    }
}

2.8 适配器模式-Adapter

定义

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作

优点

  • 客户端通过适配器可以透明地调用目标接口。
  • 复用了现存的类,程序员不需要修改原有代码而复用现有的适配者类。
  • 将目标类与适配者类解耦,解决了目标类和适配者类接口不一致的问题。
  • 在很多业务场景中符合开闭原则。

缺点

  • 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
  • 增加代码阅读难度,过多使用适配器会使系统代码变得凌乱。

结构

  • 目标接口(Target):当前系统业务所期待的接口,可以是抽象类或者接口。
  • 适配者类(Adaptee):被访问和适配的现存组件库中的组件接口。
  • 适配器类(Adapter):转换器,通过继承或引用适配者的对象,把适配者接口转换目标接口,让客户端目标接口的格式访问适配者。

实现

package top.parak.adapter.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.adapter.pattern </p>
 * <p> FileName: Adapter <p>
 * <p> Description: 适配器模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/18
 */

import org.apache.log4j.Logger;

/**
 * <P>测试类</p>
 */
public class AdapterDemo {
    public static void main(String[] args) {
        Target target = new Adapter();
        target.request();
    }
}

/**
 * <p>
 *     目标接口
 * </p>
 */
interface Target {
    public void request();
}

/**
 * <p>
 *     适配者接口
 * </p>
 */
class Adaptee {
    private Logger log = Logger.getLogger(Adaptee.class);
    public void specificRequest() {
        log.info("适配者中的业务代码被调用");
    }
}

/**
 * <p>
 *     适配器类
 * </p>
 */
class Adapter extends Adaptee implements Target{
    @Override
    public void request() {
        specificRequest();
    }
}

2.9 桥接模式-Bridge

定义

将抽象与实现分离,使它们可以独立变化。

优点

  • 抽象与实现分离,扩展能力强。
  • 符合开闭原则。
  • 符合合成复用原则。
  • 其实现细节对客户透明。

缺点

  • 由于聚合关系建立在抽象层,要求开发者对抽象化进行设计与编程,能正确的识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。

结构

  • 抽象化角色(Abstraction):定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化角色(Refined Abstraction):是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化角色(Implementor):定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化角色(Concrete Implementor):给出实现化角色接口的具体实现。

实现

package top.parak.bridge.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.bridge.pattern </p>
 * <p> FileName: Bridge <p>
 * <p> Description: 桥接模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/18
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class BridgeDemo {
    public static void main(String[] args) {
        Implementor implementor = new ConcreteImplementorA();
        Abstraction abstraction = new RefinedAbstraction(implementor);
        abstraction.operation();
    }
}

/**
 * <p>
 *     实现化角色
 * </p>
 */
interface Implementor {
    public void operationImpl();
}

/**
 * <p>
 *     具体实现化角色
 * </p>
 */
class ConcreteImplementorA implements Implementor {
    private Logger log = Logger.getLogger(ConcreteImplementorA.class);
    @Override
    public void operationImpl() {
        log.info("具体实现化角色(Concrete Implementor)被访问");
    }
}

/**
 * <p>
 *     抽象化角色
 * </p>
 */
abstract class Abstraction {
    protected Implementor implementor;

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

    public abstract void operation();
}

/**
 * <p>
 *     扩展抽象化角色
 * </p>
 */
class RefinedAbstraction extends Abstraction {
    private Logger log = Logger.getLogger(RefinedAbstraction.class);

    protected RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void operation() {
        log.info("扩展抽象化角色(Refined Abstraction)被访问");
        implementor.operationImpl();
    }
}

2.10 装饰模式-Decorator

定义

在不改变现有对象结构的情况下,动态地给该对象增加一些职责的模式。

优点

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果。
  • 装饰模式完全遵守开闭原则。

缺点

  • 装饰模式会增加许多子类,过度使用会增加程序的复杂性。

结构

  • 抽象构建角色(Component):定义一个抽象接口以规范准备接附加责任的对象。
  • 具体构建角色(Concrete Component):实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰角色(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰角色(Concrete Decorator):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

实现

package top.parak.decorator.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.decorator.pattern </p>
 * <p> FileName: Decorator <p>
 * <p> Description: 装饰模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/18
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class DecoratorDemo {
    public static void main(String[] args) {
        Component component1 = new ConcreteComponent();
        component1.operation();
        Component component2 = new ConcreteDecorator(component1);
        component2.operation();
    }
}

/**
 * <p>
 *     抽象构件角色
 * </p>
 */
interface Component {
    public void operation();
}

/**
 * <p>
 *     具体构件角色
 * </p>
 */
class ConcreteComponent implements Component {
    private Logger log = Logger.getLogger(ConcreteComponent.class);
    public ConcreteComponent() {
        log.info("创建具体构件角色");
    }

    @Override
    public void operation() {
        log.info("调用具体构件角色的方法operation()");
    }
}

/**
 * <p>
 *     抽象装饰角色
 * </p>
 */
class Decorator implements Component {
    private Component component;

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

    @Override
    public void operation() {

    }
}

/**
 * <p>
 *     具体装饰角色
 * </p>
 */
class ConcreteDecorator extends Decorator {
    private Logger log = Logger.getLogger(ConcreteComponent.class);

    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        addFunction();
    }

    public void addFunction() {
        log.info("为具体构件角色增加额外的功能function()");
    }
}

2.11 外观模式-Facade

定义

通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问。外观模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

优点

  • 降低了子系统与客户端之间的耦合度,使得子系统高的变化不会影响调用它的客户类。
  • 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
  • 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

缺点

  • 不能很好地限制客户使用子系统类,很容易带来未知风险。
  • 增加新的子系统可能需要修改外观类或客户端的源代码,违背了开闭原则。

结构

  • 外观角色(Facade):为多个子系统对外提供一个共同的接口。
  • 子系统角色(Sub System):实现系统的部分功能,客户可以通过外观角色访问它。
  • 客户角色(Client):通过一个外观角色访问各个子系统的功能。

实现

package top.parak.facade.pattern;

import org.apache.log4j.Logger;

/**
 * <p> Project: DesignPattern </P>
 * <p> Package: top.parak.facade.pattern </p>
 * <p> FileName: Facade <p>
 * <p> Description: 外观模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/19
 */

/**
 * <p>测试类</p>
 */
public class FacadeDemo {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.method();
    }
}

class Facade {
    private SubSys1 subSys1 = new SubSys1();
    private SubSys2 subSys2 = new SubSys2();
    private SubSys3 subSys3 = new SubSys3();
    public void method() {
        subSys1.method1();
        subSys2.method2();
        subSys3.method3();
    }
}

class SubSys1 {
    private Logger log = Logger.getLogger(SubSys1.class);
    public void method1() {
        log.info("子系统1的method1被调用");
    }
}

class SubSys2 {
    private Logger log = Logger.getLogger(SubSys2.class);
    public void method2() {
        log.info("子系统2的method2被调用");
    }
}


class SubSys3 {
    private Logger log = Logger.getLogger(SubSys3.class);
    public void method3() {
        log.info("子系统3的method3被调用");
    }
}


2.12 享元模式-Flyweight

定义

运用共享技术来有效地支持大量细粒度对象的复用。

优势

相同对象只保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

缺点

  • 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
  • 读取享元模式的外部状态会使得运行时间稍微变长。

状态

  • 内部状态:不会随着环境的改变而改变的可共享部分。
  • 外部状态:随着环境改变而改变的不可以共享的部分。

结构

  • 抽象享元角色(Flyweight):所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
  • 具体享元角色(Concrete Flyweight):实现抽象享元角色中所规定的接口。
  • 非享元角色(Unsharable Flyweight):是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
  • 享元工厂角色(Flyweight Factory):负责创建和管理享元角色。当客户对象请求的时候,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

实现

package top.parak.flyweight.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.flyweight.pattern </p>
 * <p> FileName: Flyweight <p>
 * <p> Description: 享元模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/19
 */

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.log4j.Logger;

import java.util.HashMap;

/**
 * <P>测试类</p>
 */
public class FlyweightDemo {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight f1 = factory.getFlyweight("K");
        Flyweight f2 = factory.getFlyweight("H");
        Flyweight f3 = factory.getFlyweight("I");
        Flyweight f4 = factory.getFlyweight("G");
        Flyweight f5 = factory.getFlyweight("H");
        f1.operation(new UnsharedConcreteFlyweight("第一次调用K"));
        f2.operation(new UnsharedConcreteFlyweight("第一次调用H"));
        f3.operation(new UnsharedConcreteFlyweight("第一次调用I"));
        f4.operation(new UnsharedConcreteFlyweight("第一次调用G"));
        f5.operation(new UnsharedConcreteFlyweight("第二次调用H"));
    }
}

/**
 * <p>
 *     非享元角色
 * </p>
 */
@AllArgsConstructor
@Setter
@Getter
class UnsharedConcreteFlyweight {
    private String info;
}

/**
 * <p>
 *     抽象享元角色
 * </p>
 */
interface Flyweight {
    public void operation(UnsharedConcreteFlyweight concreteFlyweight);
}

/**
 * <p>
 *     具体享元角色
 * </p>
 */
class ConcreteFlyweight implements Flyweight {
    private Logger log = Logger.getLogger(ConcreteFlyweight.class);
    private String key;
    public ConcreteFlyweight(String key) {
        this.key = key;
        log.info("具体享元" + key + "被创建");
    }

    @Override
    public void operation(UnsharedConcreteFlyweight concreteFlyweight) {
        log.info("具体享元" + key + "被调用");
        log.info("非享元信息是:" + concreteFlyweight.getInfo());
    }
}

/**
 * <p>
 *     享元工厂角色
 * </p>
 */
class FlyweightFactory {
    private Logger log = Logger.getLogger(FlyweightFactory.class);
    private HashMap<String, Flyweight> flyweightMap = new HashMap<String, Flyweight>();
    public Flyweight getFlyweight(String key) {
        Flyweight flyweight = (Flyweight) flyweightMap.get(key);
        if (flyweight != null) {
            log.info("具体享元" + key + "已经存在,被成功获取!");
        } else {
            flyweight = new ConcreteFlyweight(key);
            flyweightMap.put(key, flyweight);
        }
        return flyweight;
    }
}

2.13 组合模式-Composite

定义

有时又叫做部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示”部分-整体”的关系,使用户对单个组合对象具有一致的访问性。

优点

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。
  • 更容易在组合体内加入了新的对象,客户端不会因为加入了新的对象而更改源代码,满足开闭原则。

缺点

  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系。
  • 不容易限制容器中的构件。
  • 不容易用继承的方法来增加构件的新功能。

结构

  • 抽象构件角色(Component):它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为,在透明式的组合模式中抽象插件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
  • 树叶构件角色(Leaf):是组合模式中的叶子结点,没有子结点,用于实现抽象构件角色中声明的公共接口。
  • 树枝构件角色(Composite):是组合模式中的分支结点对象,有子结点,它实现了抽象构件角色中声明的接口,主要作用是存储和管理子部件,通常包含add()、remove()、getChild()等方法。

实现

package top.parak.composite.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.composite.pattern </p>
 * <p> FileName: Composite <p>
 * <p> Description: 组合模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/19
 */

import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>测试类</p>
 */
public class CompositeDemo {
    public static void main(String[] args) {
        Component component0 = new Composite();
        Component component1 = new Composite();
        Component component2 = new Leaf("1");
        Component component3 = new Leaf("2");
        Component component4 = new Leaf("3");
        component0.add(component2);
        component0.add(component1);
        component1.add(component3);
        component1.add(component4);
        component0.operation();
    }
}

/**
 * <p>
 *     抽象构件
 * </p>
 */
interface Component {
    public void add(Component c);
    public void remove(Component c);
    public Component getChild(int i);
    public void operation();
}

/**
 * <p>
 *     树叶构件
 * </p>
 */
class Leaf implements Component {
    private Logger log = Logger.getLogger(Leaf.class);
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void add(Component c) { }

    @Override
    public void remove(Component c) { }

    @Override
    public Component getChild(int i) {
        return null;
    }

    @Override
    public void operation() {
        log.info("树叶" + name + "被访问");
    }
}
     **
 * <p>
 *     树枝构件
 * </p>
 */
class Composite implements Component{
    private List<Component> children = new ArrayList<>();

    @Override
    public void add(Component c) {
        children.add(c);
    }

    @Override
    public void remove(Component c) {
        children.remove(c);
    }

    @Override
    public Component getChild(int i) {
        return children.get(i);
    }

    @Override
    public void operation() {
        for (Object obj : children) {
            ((Component) obj).operation();
        }
    }
}

2.14 模板方法模式-Template Method

定义

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

优点

  • 封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类实现中,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

缺点

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

结构

  • 抽象类(Abstract class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成
    • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法
    • 基本方法:
      • 抽象方法:在抽象类中申明,由具体子类中可以继承或重写它。
      • 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
      • 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的孔方法两种。
  • 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。

实现

package top.parak.templateMethod.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.templateMethod.pattern </p>
 * <p> FileName: TemplateMethodDemo <p>
 * <p> Description: <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/7
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class TemplateMethodDemo {
    public static void main(String[] args) {
        AbstractClass abstractClass = new ConcreteClass();
        abstractClass.templateMethod();
    }
}

/**
 * <P>
 *     抽象类
 * </p>
 */
abstract class AbstractClass {
    private Logger log = Logger.getLogger(AbstractClass.class);

    /* 模板方法 */
    public void templateMethod() {
        specificMethod();
        abstractMethod1();
        abstractMethod2();
    }

    /* 具体方法 */
    public void specificMethod() {
        log.info("具体方法被调用");
    }

    /* 抽象方法1 */
    public abstract void abstractMethod1();
    /* 抽象方法2 */
    public abstract void abstractMethod2();
}

class ConcreteClass extends AbstractClass {
    private Logger log = Logger.getLogger(ConcreteClass.class);

    @Override
    public void abstractMethod1() {
        log.info("抽象方法1的实现被调用");
    }

    @Override
    public void abstractMethod2() {
        log.info("抽象方法2的实现被调用");
    }
}

2.15 策略模式-Strategy

定义

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互依赖,且算法的变化不会影响到使用算法的用户。

优点

  • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
  • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  • 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点

  • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  • 策略模式造成很多的策略类。

结构

  • 抽象策略类(Strategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 具体策略类(Concrete Strategy):实现了抽象了策略定义的接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 环境类(Context):持有一个策略类的引用,最终给客户端调用。

实现

package top.parak.strategy.pattern;

import org.apache.log4j.Logger;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.strategy.pattern </p>
 * <p> FileName: StrategyDemo <p>
 * <p> Description: 策略模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/7
 */

/**
 * <p>测试类</p>
 */
public class StrategyDemo {
    public static void main(String[] args) {
        Context context = new Context();
        Strategy strategy1 = new ConcreteStrategyA();
        Strategy strategy2 = new ConcreteStrategyB();
        context.setStrategy(strategy1);
        context.strategyMethod();
        context.setStrategy(strategy2);
        context.strategyMethod();
    }
}

/**
 * <p>
 *     抽象策略类
 * </p>
 */
interface Strategy {
    void strategyMethod();
}

/**
 * <p>
 *     具体策略类A
 * </p>
 */
class ConcreteStrategyA implements Strategy {
    private Logger log = Logger.getLogger(ConcreteStrategyA.class);
    @Override
    public void strategyMethod() {
        log.info("具体策略A的策略方法被访问");
    }
}

/**
 * <p>
 *     具体策略类B
 * </p>
 */
class ConcreteStrategyB implements Strategy {
    private Logger log = Logger.getLogger(ConcreteStrategyB.class);
    @Override
    public void strategyMethod() {
        log.info("具体策略B的策略方法被访问");
    }
}

/**
 * <p>
 *     环境类
 * </p>
 */
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

2.16 命令模式-Command

定义

将请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

优点

  • 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
  • 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足开闭原则,对拓展比较灵活。
  • 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  • 方便实现Undo和Redo操作。命令模式可以与备忘录模式结合,实现命令的撤销与恢复。

缺点

可能产生大量具体命令类。因为对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。

结构

  • 抽象命令类(Command):声明执行命令的接口,拥有执行命令的抽象方法execute()。
  • 具体命令角色(Concrete Command):是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  • 接收者(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  • 请求者(Invoker):请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象类执行相关请求,它不张子杰访问接收者。

实现

package top.parak.command.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.command.pattern </p>
 * <p> FileName: CommandDemo <p>
 * <p> Description: 命令模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/7
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class CommandDemo {
    public static Logger log = Logger.getLogger(CommandDemo.class);
    public static void main(String[] args) {
        Command command = new ConcreteCommand();
        Invoker invoker = new Invoker(command);
        log.info("客户访问调用者的call方法");
        invoker.call();
    }
}

/**
 * <p>
 *     调用者
 * </p>
 */
class Invoker {
    private Logger log = Logger.getLogger(Invoker.class);
    private Command command;
    public Invoker(Command command)  {
        this.command = command;
    }
    public void setCommand(Command command) {
        this.command = command;
    }
    public void call() {
        log.info("调用者执行命令command");
        command.execute();
    }
}

/**
 * <p>
 *     抽象命令
 * </p>
 */
interface Command {
    abstract void execute();
}

/**
 * <p>具体命令</p>
 */
class ConcreteCommand implements Command {
    private Receiver receiver;
    public ConcreteCommand() {
        this.receiver = new Receiver();
    }
    @Override
    public void execute() {
        receiver.action();
    }
}

/**
 * <p>接收者</p>
 */
class Receiver {
    private Logger log = Logger.getLogger(Receiver.class);
    public void action() {
        log.info("接收者的action方法被调用");
    }
}

2.17 责任链模式-Chain of Responsibility

定义

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可以将请求沿着这条链传递,直到有对象处理它为止。

优点

  • 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  • 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if-else语句。
  • 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一个影响。
  • 职责链的建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于责任链的错误设置而导致系统出错,如可能还会造成循环调用。

结构

  • 抽象处理者(Handler):定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

  • 具体处理者(Concrete Handler):实现抽象处理者的处理方法,判断能否本次请求,如果可以处理请求则处理,否则将给请求转给它的后继者。

  • 客户类(Client):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

实现

package top.parak.chainofResposibility.example;

import org.apache.log4j.Logger;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.chainofResposibility.example </p>
 * <p> FileName: Main <p>
 * <p> Description: 责任链Demo <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/23
 */

/**
 * <p>
 *     测试类
 * </p>
 */
public class Main {
    public static void main(String[] args) {
        Support RubbishK = new NoSupport("RubbishK");
        Support FlowerK = new LimitSupport("FlowerK", 100);
        Support EyedropK = new LimitSupport("EyedropK", 200);
        Support UnknownK = new SpecialSupport("UnknownK", 300);
        Support KHighness = new SpecialSupport("KHighness", 330);
        Support BrotherK = new OddSupport("BrotherK");
        // 制造责任链
        RubbishK.setNext(FlowerK).setNext(EyedropK).setNext(UnknownK).setNext(KHighness).setNext(BrotherK);
        // 制造问题
        for (int i = 0; i < 500; i += 30) {
            RubbishK.support(new Trouble(i));
        }
    }
}

/**
 * <p>
 *     发生的问题的类
 * </p>
 */
class Trouble {
    private int number;

    public Trouble(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    @Override
    public String toString() {
        return "[Trouble " +
                "number=" + number +
                ']';
    }
}

/**
 * <p>
 *     用来解决问的抽象类
 * </p>
 */
abstract class Support {
    private Logger log = Logger.getLogger(Support.class);

    private String name;
    private Support next;

    public Support(String name) {
        this.name = name;
    }

    public Support setNext(Support next) {
        this.next = next;
        return next;
    }

    @Override
    public String toString() {
        return "[" + name + "]";
    }

    public final void support(Trouble trouble) {
        if (resolve(trouble)) {
            done(trouble);
        } else if (next != null) {
            next.support(trouble);
        } else {
            fail(trouble);
        }
    }

    public abstract boolean resolve(Trouble trouble);

    protected void done(Trouble trouble) {
        log.info(trouble + " is resolved by " + this + ".");
    }

    protected void fail(Trouble trouble) {
        log.info(trouble + " cannot be resolved.");
    }
}

/**
 * <p>
 *     解决问题具体类:不能解决问题
 * </p>
 */
class NoSupport extends Support {
    public NoSupport(String name) {
        super(name);
    }

    @Override
    public boolean resolve(Trouble trouble) {
        return false;
    }
}

/**
 * <p>
 *     解决问题具体类:解决问题编号小于limit值得类
 * </p>
 */
class LimitSupport extends Support {
    private int limit;

    public LimitSupport(String name, int limit) {
        super(name);
        this.limit = limit;
    }

    @Override
    public boolean resolve(Trouble trouble) {
        if (trouble.getNumber() < limit) {
            return true;
        } else {
            return false;
        }
    }
}

/**
 * <p>
 *     解决问题具体类:解决奇数编号得问题
 * </p>
 */
class OddSupport extends Support {
    public OddSupport(String name) {
        super(name);
    }

    @Override
    public boolean resolve(Trouble trouble) {
        if (trouble.getNumber() % 2 != 0) {
            return true;
        } else {
            return false;
        }
    }
}

/**
 * <p>
 *     解决问题具体类:只解决指定编号得问题
 * </p>
 */
class SpecialSupport extends Support {
    private int number;

    public SpecialSupport(String name, int number) {
        super(name);
        this.number = number;
    }

    @Override
    public boolean resolve(Trouble trouble) {
        if (trouble.getNumber() == this.number) {
            return true;
        } else {
            return false;
        }
    }
}

2.18 状态模式-State

定义

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

优点

  • 状态模式与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足单一职责原则。
  • 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态变得更加明确,切减少对象间的相互依赖。
  • 有利于程序的拓展。通过定义新的子类很容易德增加新的状态和转换。

缺点

  • 状态模式的使用必然会增加系统的类与对象的个数。
  • 状态模式的结构和实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

结构

  • 环境类(Context):也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作都委托给当前状态对象类处理。
  • 抽象状态类(State):定义一个接口,用以封装环境对象中特定状态所对应的行为。
  • 具体状态类(Concrete State):实现抽象状态所对应的行为。

实现

'package top.parak.state.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.state.pattern </p>
 * <p> FileName: StateDemo <p>
 * <p> Description: 状态模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/8
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class StateDemo {
    public static void main(String[] args) {
        Context context = new Context();
        context.handle();
        context.handle();
        context.handle();
        context.handle();
        context.handle();
    }
}

/**
 * <p>
 *     环境类
 * </p>
 */
class Context {
    private State state;
    /* 初始状态 */
    public Context()  {
        this.state = new ConcreteStateA();
    }
    /* 读取状态 */
    public State getState() {
        return state;
    }
    /* 设置状态 */
    public void setState(State state) {
        this.state = state;
    }
    /* 处理请求 */
    public void handle()  {
        state.handle(this);
    }
}

/**
 * <p>
 *     抽象状态类
 * </p>
 */
abstract class State {
    abstract void handle(Context context);
}

/**
 * <p>
 *     具体状态A类
 * </p>
 */
class ConcreteStateA extends State {
    private Logger log = Logger.getLogger(ConcreteStateA.class);

    @Override
    void handle(Context context) {
        log.info("当前状态:A");
        context.setState(new ConcreteStateB());
    }
}

/**
 * <p>
 *     具体状态B类
 * </p>
 */
class ConcreteStateB extends State {
    private Logger log = Logger.getLogger(ConcreteStateB.class);

    @Override
    void handle(Context context) {
        log.info("当前状态:B");
        context.setState(new ConcreteStateA());
    }
}'

2.19 观察者模式-Observer

定义

多个对象之间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,又叫做发布-订阅模式。

优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。

  • 目标与观察者之间建立了一套触发机制。

缺点

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

结构

  • 抽象主题(Subject):提供了一个用于保存观察者对象的聚集类和增加、增加观察者对象的方法,以及通知所有观察者的抽象方法。
  • 具体主题(Concrete Subject):实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  • 抽象观察者(Observer):一个抽象类或者接口,包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  • 具体观察者(Concrete Observer):实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

实现

package top.parak.observer.pattern;

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.observer.pattern </p>
 * <p> FileName: ObserverDemo <p>
 * <p> Description: 观察者模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/27
 */

public class ObserverDemo {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer[] observers = new Observer[20];
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 1) {
                observers[i] = new ConcreteObserver1();
            } else {
                observers[i] = new ConcreteObserver2();
            }
            subject.add(observers[i]);
        }
        subject.notifyObserver();
    }
}

/**
 * <p>
 *     抽象目标
 * </p>
 */
abstract class Subject {
    protected List<Observer> observers = new ArrayList<Observer>();
    /* 增加观察者 */
    public void add(Observer observer) {
        observers.add(observer);
    }
    /* 删除观察者 */
    public void del(Observer observer) {
        observers.remove(observer);
    }
    /* 通知观察者 */
    public abstract void notifyObserver();
}

/**
 * <p>
 *     具体目标
 * </p>
 */
class ConcreteSubject extends Subject {
    @Override
    public void notifyObserver() {
        System.out.println("具体目标发生改变");
        System.out.println("——————————————");
        for (Observer observer : observers) {
            observer.response();
        }
    }
}

/**
 * <p>
 *     抽象观察者
 * </p>
 */
interface Observer {
    /* 作出反应 */
    void response();
}

/**
 * <p>
 *     具体观察者1
 * </p>
 */
class ConcreteObserver1 implements Observer {
    private Logger log = Logger.getLogger(ConcreteObserver1.class);
    @Override
    public void response() {
        log.info("具体观察者1作出反应");
    }
}

/**
 * <P>
 *     具体观察者2
 * </p>
 */
class ConcreteObserver2 implements Observer {
    private Logger log = Logger.getLogger(ConcreteObserver2.class);
    @Override
    public void response() {
        log.info("具体观察者2作出反应");
    }
}

2.20 中介者模式-Mediator

定义

定义一个中介对象来封装一系列对象之间的交互,使原有对象直接的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式。

优点

  • 降低了对象之间的耦合性,使得对象易于独立地被复用。
  • 将对象之间的一对多关联转为一对一的关联,提高系统的灵活性,使得系统易于维护和拓展。

缺点

  • 当同事类太多时,中介者的职责职责将很大,它会变得复杂而庞大,以至于系统难以维护。

结构

  • 抽象中介者(Mediator):它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  • 具体中介者(Concrete Mediator):实现中介者接口,定义一个List来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  • 抽象同事类(Colleague):定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  • 具体同事类(Concrete Colleague):是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

实现

package top.parak.mediator.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.mediator.pattern </p>
 * <p> FileName: MediatorDemo <p>
 * <p> Description: <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/8
 */

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *     中介者
 * </p>
 */
public class MediatorDemo {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();
        Colleague colleague1, colleague2;
        colleague1 = new ConcreteColleague1();
        colleague2 = new ConcreteColleague2();
        mediator.register(colleague1);
        mediator.register(colleague2);
        colleague1.send();
        colleague2.send();
    }
}

/**
 * <p>
 *     中介者
 * </p>
 */
abstract class Mediator {
    /* 注册 */
    public abstract void register(Colleague colleague);
    /* 转发 */
    public abstract void relay(Colleague colleague);
}

/**
 * <p>
 *     具体中介者
 * </p>
 */
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<>();

    @Override
    public void register(Colleague colleague) {
        colleagues.add(colleague);
        colleague.setMediator(this);
    }

    @Override
    public void relay(Colleague colleague) {
        for (Colleague colleague1 : colleagues) {
            if (!colleague1.equals(colleague)) {
                colleague1.receive();
            }
        }
    }
}

/**
 * <p>
 *     抽象同事类
 * </p>
 */
abstract class Colleague {
    private Logger log = Logger.getLogger(Colleague.class);
    protected Mediator mediator;

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    public abstract void receive();
    public abstract void send();
}

/**
 * <p>
 *     具体同事类1
 * </p>
 */
class ConcreteColleague1 extends Colleague {
    private Logger log = Logger.getLogger(ConcreteColleague1.class);

    @Override
    public void receive() {
        log.info("具体同事类1收到请求");
    }

    @Override
    public void send() {
        log.info("具体同事1发出请求");
        mediator.relay(this);
    }
}

/**
 * <p>
 *     具体同事类2
 * </p>
 */
class ConcreteColleague2 extends Colleague {
    private Logger log = Logger.getLogger(ConcreteColleague2.class);

    @Override
    public void receive() {
        log.info("具体同事类2收到请求");
    }

    @Override
    public void send() {
        log.info("具体同事2发出请求");
        mediator.relay(this);
    }
}

2.21迭代器模式-Iterator

定义

提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

优点

  • 访问一个聚合对象的内容而无须暴露它的内部表示。
  • 遍历任务交由迭代器完成,这简化了聚合类。
  • 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
  • 增加新的聚合类和迭代器都很方便,无须修改原有代码。
  • 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

缺点

  • 增加了类的个数,这在一定程度上增加了系统的复杂性。

结构

  • 抽象聚合角色(Aggregate):定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  • 具体聚合色(Concrete Aggregate):实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器角色(Iterator):定义访问和遍历聚合元素的接口,通常包含hasNext()、first()、next()等方法。
  • 具体迭代器角色(Concrete Iterator):实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

实现

package top.parak.iterator.pattern;

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.iterator.pattern </p>
 * <p> FileName: IteratorDemo <p>
 * <p> Description: 迭代器模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/10/27
 */

public class IteratorDemo {
    public static Logger log = Logger.getLogger(IteratorDemo.class);

    public static void main(String[] args) {
        Aggregate aggregate = new ConcreteAggregate();
        aggregate.add("KHighness");
        aggregate.add("ParaK");
        aggregate.add("FlowerK");
        log.info("聚合的内容");
        Iterator iterator = aggregate.getIterator();
        log.info(iterator.first().toString());
        while (iterator.hasNext()) {
            log.info(iterator.next().toString());
        }
    }
}

/**
 * <p>
 *     抽象聚合
 * </p>
 */
interface Aggregate {
    public void add(Object o);
    public void del(Object o);
    public Iterator getIterator();
}

/**
 * <p>
 *     具体聚合
 * </p>
 */
class ConcreteAggregate implements Aggregate {
    private List<Object> list = new ArrayList<>();

    @Override
    public void add(Object o) {
        list.add(o);
    }

    @Override
    public void del(Object o) {
        list.remove(o);
    }

    @Override
    public Iterator getIterator() {
            return (new ConcreteIterator(list));
    }
}

/**
 * <p>
 *     抽象迭代器
 * </p>
 */
interface Iterator {
    Object first();
    Object next();
    boolean hasNext();
}

/**
 * <p>
 *     具体迭代器
 * </p>
 */
class ConcreteIterator implements Iterator {

    private List<Object> list = null;
    private int index = 1;

    public ConcreteIterator(List<Object> list) {
        this.list = list;
    }

    @Override
    public Object first() {
        index = 0;
        Object object = list.get(index);
        return object;
    }

    @Override
    public Object next() {
        Object object = null;
        if (this.hasNext()) {
            object = list.get(++index);
        }
        return object;
    }

    @Override
    public boolean hasNext() {
        if (index < list.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
}

2.22 访问者模式-Visitor

定义

将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作。

优点

  • 扩展性好
  • 复用性好
  • 灵活性好
  • 符合单一职责原则

缺点

  • 增加新的元素很困难
  • 破坏封装
  • 违反了依赖倒置原则

结构

  • 抽象访问者(Visitor):定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作visit(),该操作中的参数类型标识了被访问的具体元素。
  • 具体访问者(Concrete Visitor):实现抽象访问者角色声明的各个访问操作,确定访问者访问一个元素时该做什么。
  • 抽象元素(Element):声明一个包含接受操作accept()的接口,被接受的访问者对象作为accept()的参数。
  • 具体元素(Concrete Element):实现抽象元素角色提供的accept()操作。
  • 对象结构(Object Structure):包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法。

实现

package top.parak.visitor.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.visitor.pattern </p>
 * <p> FileName: VisitorDemo <p>
 * <p> Description: 访问者模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/8
 */

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * <p>测试类</p>
 */
public class VisitorDemo {
    public static void main(String[] args) {
        ObjectStructure structure = new ObjectStructure();
        structure.add(new ConcreteElementA());
        structure.add(new ConcreteElementB());
        structure.add(new ConcreteElementC());

        Visitor visitorA = new ConcreteVisitorA();
        Visitor visitorB = new ConcreteVisitorB();
        structure.accept(visitorA);
        structure.accept(visitorB);
    }
}

/**
 * <p>
 *     抽象访问者
 * </p>
 */
interface Visitor {
    void visit(ConcreteElementA elementA);
    void visit(ConcreteElementB elementB);
    void visit(ConcreteElementC elementC);
}

/**
 * <p>
 *     具体访问者A
 * </p>
 */
class ConcreteVisitorA implements Visitor {
    private Logger log = Logger.getLogger(ConcreteVisitorA.class);

    @Override
    public void visit(ConcreteElementA elementA) {
        log.info("具体访问者A访问=>具体元素A");
        elementA.operaA();
    }

    @Override
    public void visit(ConcreteElementB elementB) {
        log.info("具体访问者A访问=>具体元素B");
        elementB.operaB();
    }

    @Override
    public void visit(ConcreteElementC elementC) {
        log.info("具体访问者A访问=>具体元素C");
        elementC.operaC();
    }
}

/**
 * <p>
 *     具体访问者B
 * </p>
 */
class ConcreteVisitorB implements Visitor {
    private Logger log = Logger.getLogger(ConcreteVisitorB.class);

    @Override
    public void visit(ConcreteElementA elementA) {
        log.info("具体访问者B访问=>具体元素A");
        elementA.operaA();
    }

    @Override
    public void visit(ConcreteElementB elementB) {
        log.info("具体访问者B访问=>具体元素B");
        elementB.operaB();
    }

    @Override
    public void visit(ConcreteElementC elementC) {
        log.info("具体访问者B访问=>具体元素C");
        elementC.operaC();
    }
}

/**
 * <p>
 *     抽象元素类
 * </p>
 */
interface Element {
    void accept(Visitor visitor);
}

/**
 * <p>
 *     具体元素A
 * </p>
 */
class ConcreteElementA implements Element {
    private Logger log = Logger.getLogger(ConcreteElementA.class);

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void operaA()  {
        log.info("具体元素A操作...");
    }
}

/**
 * <p>
 *     具体元素B
 * </p>
 */
class ConcreteElementB implements Element {
    private Logger log = Logger.getLogger(ConcreteElementB.class);

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void operaB()  {
        log.info("具体元素B操作...");
    }
}

/**
 * <p>
 *     具体元素C
 * </p>
 */
class ConcreteElementC implements Element {
    private Logger log = Logger.getLogger(ConcreteElementC.class);

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void operaC()  {
        log.info("具体元素C操作...");
    }
}

/**
 * <p>
 *     对象结构角色
 * </p>
 */
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();

    public void accept(Visitor visitor) {
        Iterator<Element> iterator = list.iterator();
        while (iterator.hasNext()) {
            iterator.next().accept(visitor);
        }
    }

    public void add(Element element) {
        list.add(element);
    }

    public void remove(Element element) {
        list.remove(element);
    }
}

2.23 备忘录模式-Memento

定义

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

优点

  • 提供了一个可以恢复状态的机制
  • 实现内部状态的封装
  • 简化了发起人

缺点

  • 资源消耗大

结构

  • 发起人(Originator):记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • 备忘录(Memento):负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • 管理者(Caretaker):对备忘录进行管理,提供保存与获取备忘录的功能,但不能对备忘录进行访问与修改。

实现

package top.parak.memento.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.memento.pattern </p>
 * <p> FileName: MementoDemo <p>
 * <p> Description: 备忘录模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/8
 */

import org.apache.log4j.Logger;

/**
 * <p>测试类</p>
 */
public class MementoDemo {
    public static Logger log = Logger.getLogger(MementoDemo.class);
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();
        originator.setState("State1");
        log.info("初始状态:" + originator.getState());
        // 保存状态
        caretaker.setMemento(originator.createMemento());
        originator.setState("State2");
        log.info("新的状态:" + originator.getState());
        // 恢复状态
        originator.restoreMemento(caretaker.getMemento());
        log.info("恢复状态:" + originator.getState());
    }
}

/**
 * <p>
 *     备忘录
 * </p>
 */
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;
    }
}

/**
 * <p>
 *     发起人
 * </p>
 */
class Originator {
    private String state;

    public String getState() {
        return state;
    }

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

    public Memento createMemento() {
        return new Memento(state);
    }

    public void restoreMemento(Memento memento) {
        this.setState(memento.getState());
    }
}

/**
 * <p>
 *     管理者
 * </p>
 */
class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

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

2.24 解释器模式-Interpreter

定义

给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释其中的句子。

优点

  • 扩展性好
  • 容易实现

缺点

  • 执行效率较低
  • 会引起类膨胀
  • 可应用的场景比较少

结构

  • 抽象表达式(Abstract Expression):定义解释器的接口,约定解释器的解释操作,主要包含解释方法interpret()。
  • 终结符表达式(Terminal Expression):抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每条一个终结符都对应于一个非终结符表达式。
  • 非终结符表达式(Terminal Expression):抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context):通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后开你的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

实现

package top.parak.interpreter.pattern;

/**
 * <p> Project: DesignPattern </p>
 * <p> Package: top.parak.interpreter.pattern </p>
 * <p> FileName: InterpreterDemo <p>
 * <p> Description: 解释器模式 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2020/11/8
 */

/**
 * <p>
 *     测试类
 * </p>
 */
public class InterpreterDemo {
}

/**
 * <p>
 *     抽象表达式
 * </p>
 */
interface AbstractExpression {
    Object interpret(String info);
}

/**
 * <p>
 *     终结符表达式类
 * </p>
 */
class TerminalExpression implements AbstractExpression {
    @Override
    public Object interpret(String info) {
        /* 对终结符表达式的处理 */
        return null;
    }
}

/**
 * <p>
 *     非终结符表达式类
 * </p>
 */
class NonTerminalExpression implements AbstractExpression {
    @Override
    public Object interpret(String info) {
        /* 对非终结符表达式的处理 */
        return null;
    }
}

/**
 * <p>
 *     环境类
 * </p>
 */
class Context {
    private AbstractExpression abstractExpression;
    public Context() {
        /* 数据初始化 */
    }
    public void operation(String info) {
        /* 调用相关表达式的解释方法 */
    }
}