设计模式

EmiyaCC 于 2021-06-24 发布

设计模式的六大原则

  1. 单一功能原则(类和方法,接口)

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

    优点:控制类的粒度大小、将对象解耦、提高其内聚性

  2. 开闭原则(扩展开放,修改关闭)

    通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中

  3. 里氏替换原则(基类和子类之间的关系)

    实现开闭原则的重要方式之一

    子类可以扩展父类的功能,但不能改变父类原有的功能

  4. 依赖倒置原则(依赖抽象接口,而不是具体对象)

    实现开闭原则的重要途径之一

    使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成

  5. 接口隔离原则(接口按照功能细分)

    尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法

  6. 迪米特法则(类与类之间的亲疏关系)

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

设计模式六大原则详解

说一下你熟悉的设计模式?

cyc2018 设计模式

创建性

单例模式

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

// 懒汉式
public class Singleton {
	private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
// 线程安全懒汉式
public class Singleton {
	private static Singleton instance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
// DCL (Double-checked Locking, 双重检查锁定模式)
public class Singleton {
	private volatile static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
/*
* 在执行 instance = new Singleton() 时还是有安全问题,JVM 在执行改行代码时会进行分配对象的内存空间、初始化对象、instance 引用分配好的空间,但指令重排后,有可能出现的情况是初始化对象和 instance 引用两条指令交换,导致 instance 引用到未初始化的对象。如果此时刚好有线程访问,那么会直接返回一个未初始化的对象,从而导致线程安全问题的出现。
* 解决方法就是加 volatile 关键字,防止指令重排就好了。
*/

// 静态内部类
public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
// 饿汉式
public class Singleton {
	private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}
// 枚举类:上面几种方法序列化和反序列化会重新创建单例实例,而枚举l
// https://segmentfault.com/a/1190000024472352

简单工厂模式

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

// 抽象产品
public interface Product {
}
// 具体产品
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
// 简单工厂
public class SimpleFactory {

    public Product createProduct(int type) {
        if (type == 1) {
            return new ConcreteProduct1();
        }
        return new ConcreteProduct2();
    }
}
// test
public class Client {
    public static void main(String[] args) {
		SimpleFactory simpleFactory = new SimpleFactory();
		Product product = simpleFactory.createProduct(1);
    }
}

工厂方法模式

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

在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象

// 抽象产品
public interface Product {
}
// 具体产品
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
// 抽象工厂
public abstract class Factory {
    abstract public Product factoryMethod();
    public void doSomething() {
        Product product = factoryMethod();
    }
}
// 具体工厂
public class ConcreteFactory1 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct1();
    }
}
public class ConcreteFactory2 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct2();
    }
}
// test
public class Client {
    public static void main(String[] args) {
		Factory factory = new ConcreteFactory1();
		Product product = factory.create();
    }
}

抽象工厂模式

提供一个接口,用于创建 相关的对象家族

与工厂方法模式不同的是,工厂方法模式中的工厂只生产单一的产品,而抽象工厂模式中的工厂生产多个产品

public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
public abstract class AbstractFactory {
    abstract AbstractProductA createProductA();
    abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory {
    AbstractProductA createProductA() {
        return new ProductA1();
    }

    AbstractProductB createProductB() {
        return new ProductB1();
    }
}
public class ConcreteFactory2 extends AbstractFactory {
    AbstractProductA createProductA() {
        return new ProductA2();
    }

    AbstractProductB createProductB() {
        return new ProductB2();
    }
}
public class Client {
    public static void main(String[] args) {
        AbstractFactory abstractFactory = new ConcreteFactory1();
        AbstractProductA productA = abstractFactory.createProductA();
        AbstractProductB productB = abstractFactory.createProductB();
    }
}

生成器模式

封装一个对象的构造过程,并允许按步骤构造

【设计模式】建造者模式(生成器模式)

// 抽象建造者
public class AbstractStringBuilder {
    protected char[] value;
    protected int count;
    public AbstractStringBuilder(int capacity) {
        count = 0;
        value = new char[capacity];
    }
    public AbstractStringBuilder append(char c) {
        ensureCapacityInternal(count + 1);
        value[count++] = c;
        return this;
    }
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }
}
// 具体建造者
public class StringBuilder extends AbstractStringBuilder {
    public StringBuilder() {
        super(16);
    }

    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
}
// test
public class Client {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        final int count = 26;
        for (int i = 0; i < count; i++) {
            sb.append((char) ('a' + i));
        }
        System.out.println(sb.toString());
    }
}

原型模式

使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象

public abstract class Prototype {
    abstract Prototype myClone();
}
public class ConcretePrototype extends Prototype {
    private String filed;
    public ConcretePrototype(String filed) {
        this.filed = filed;
    }
    @Override
    Prototype myClone() {
        return new ConcretePrototype(filed);
    }
    @Override
    public String toString() {
        return filed;
    }
}
public class Client {
    public static void main(String[] args) {
        Prototype prototype = new ConcretePrototype("abc");
        Prototype clone = prototype.myClone();
        System.out.println(clone.toString());
    }
}

行为型

观察者模式

定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。

主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者

public interface Person {
    // 小王和小李可以通过这个接口接受小美发来的消息
    void getMessage(String s)
}
public class XiaoWang implements Person {
    private String name = "小王";
    public XiaoWang() {}
    @Override
    public void getMessage(String s) {
        System.out.println(name + "接到了小美打过来的电话,电话内容是:" + s);
    }
}
public class XiaoLi implements Person {
    private String name = "小李";
    public XiaoLi() {}
    @Override
    public void getMessage(String s) {
        System.out.println(name + "接到了小美打过来的电话,电话内容是:-->" + s);
    }
}
public class XiaoMei {
    List<Person> list = new ArrayList<Person>();
    public XiaoMei() {}
    public void addPerson(Person person) {
        list.add(person);
    }
    // 发消息给备胎
    public void notifyPerson() {
        for (Person person : list) {
            person.getMessage("你们过来吧,谁先过来谁就能陪我一起玩儿游戏!");
        }
    }
}
public class Test {
   public static void main(String[] args) {
       XiaoMei xiaoMei = new XiaoMei();
       LaoWang xiaoWang = new XiaoWang();
       LaoLi xiaoLi = new XiaoLi();
       //小王和小李在小美那里都注册了一下
       xiaoMei.addPerson(xiaoWang);
       xiaoMei.addPerson(xiaoLi);
       //小美向小王和小李发送通知
       xiaoMei.notifyPerson();
   }
}

模板模式

定义算法框架,并将一些步骤的实现延迟到子类。

通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构

public abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }
    abstract void brew();
    abstract void addCondiments();
    void boilWater() {
        System.out.println("boilWater");
    }
    void pourInCup() {
        System.out.println("pourInCup");
    }
}
public class Coffee extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("Coffee.brew");
    }
    @Override
    void addCondiments() {
        System.out.println("Coffee.addCondiments");
    }
}
public class Tea extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("Tea.brew");
    }
    @Override
    void addCondiments() {
        System.out.println("Tea.addCondiments");
    }
}
public class Client {
    public static void main(String[] args) {
        CaffeineBeverage caffeineBeverage = new Coffee();
        caffeineBeverage.prepareRecipe();
        System.out.println("-----------");
        caffeineBeverage = new Tea();
        caffeineBeverage.prepareRecipe();
    }
}

策略模式

一个类的行为或其算法可以在运行时更改,创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象

public interface Strategy {
    public int doOperation (int num1, int num2);
}
public class OperationAdd {
    @Overrride
    public int doOperation (int num1, int num2) {
        return num1 + num2;
    }
}
public class OperationSubtract {
    @Overrride
    public int doOperation (int num1, int num2) {
        return num1 - num2;
    }
}
public class Context {
    private Strategy strategy;
    public Context (Strategy strategy) {
        this.strategy = strategy;
    }
    public int executeStrategy (int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}
// test
public class Demo {
    Context context = new Context(new OperationAdd());
    System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
}

结构性

装饰者模式

为对象动态添加功能

public class Food {
    private String foodName;
    public Food() {}
    public Food(String foodName) {
        this.foodName = foodName;
    }
    public String make() {
        return foodName;
    }
}
// 面包类
public class Bread extends Food {
    private Food basicFood;
    public Bread(Food basicFood) {
        this.basicFood = basicFood;
    }
    public String make() {
        return basicFood.make() + "+面包";
    }
}
// 奶油类
public class Cream extends Food {
    private Food basicFood;
    public Cream(Food basicFood) {
        this.basicFood = basicFood;
    }
    public String make() {
        return basicFood.make() + "+奶油";
    }
}
public class Test {
   public static void main(String[] args) {
       Food food = new Bread(new Cream(new Food("香肠")));
       System.out.println(food.make());
   }
}

适配者模式

把一个类接口转换成另一个用户需要的接口

class Phone {
    public static final int V = 220;
    private VoltageAdapter adapter;
    public void charge() {
        adapter.changeVoltage();
    }
    public void setAdapter(VoltageAdapter adapter) {
        this.adapter = adapter;
    }
}
// 变压器
class VoltageAdapter {
    public void changeVoltage() {
       System.out.println("正在充电...");
       System.out.println("原始电压:" + Phone.V + "V");
       System.out.println("经过变压器转换之后的电压:" + (Phone.V - 200) + "V");
    }
}
public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone();
        VoltageAdapter adapter = new VoltageAdapter();
        phone.setAdapter(adapter);
        phone.charge();
    }
}

代理模式

控制对其它对象的访问

//代理接口
public interface ProxyInterface {
	//需要代理的是结婚这件事
	void marry();
}
// 婚庆公司
public class WeddingCompany implements ProxyInterface {
    private ProxyInterface proxyInterface;
    public WeddingCompany(ProxyInterface proxyInterface) {
        this.proxyInterface = proxyInterface;
    }
    @Override
    public void marry() {
        System.out.println("我们是婚庆公司的");
        System.out.println("我们在做结婚前的准备工作");
        System.out.println("节目彩排...");
        System.out.println("礼物购买...");
        System.out.println("工作人员分工...");
        System.out.println("可以开始结婚了");
        proxyInterface.marry();
        System.out.println("结婚完毕,我们需要做后续处理,你们可以回家了,其余的事情我们公司来做");
    }
}
// 结婚家庭
public class NormalHome implements ProxyInterface {
    @Override
    public void marry() {
        System.out.println("我们结婚啦~");
    }
}
// Test
public class Test {
    public static void main(String[] args) {
        ProxyInterface proxyInterface = new WeddingCompany(new NormalHome());
        proxyInterface.marry();
    }
}

适配器模式,装饰模式,代理模式异同

适配器的特点在于兼容,从代码上的特点来说,适配类与原有的类具有相同的接口,并且持有新的目标对象。就如同一个三孔转2孔的适配器一样,他有三孔的插头,可以插到三孔插座里,又有两孔的插座可以被2孔插头插入。适配器模式是在于对原有3孔的改造。在使用适配器模式的时候,我们必须同时持有原对象,适配对象,目标对象。。。。

装饰器模式特点在于增强,他的特点是被装饰类和所有的装饰类必须实现同一个接口,而且必须持有被装饰的对象,可以无限装饰。

代理模式的特点在于隔离,隔离调用类和被调用类的关系,通过一个代理类去调用。

总的来说就是如下三句话:

https://www.jianshu.com/p/c156b5e23e30

图说设计模式

https://design-patterns.readthedocs.io/zh_CN/latest/index.html

Reference:

  • https://mp.weixin.qq.com/s/Wahq4TnCm4Pzb6VshWma1Q