设计模式六大原则

开闭原则

总结:对拓展开放,对修改关闭
一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

单一原则

总结:一个类负责一项职责
一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因

依赖倒置原则

总结:面像接口编程
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

接口隔离原则

总结:设计接口时,最小功能单元
两个定义:
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口
类间的依赖关系应该建立在最小的接口上

里氏替换原则

总结:子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
Barbara Liskov提出:
标准定义:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。

迪米特法则

总结:低耦合,高内聚。陌生的类要出现在成员变量、方法参数、方法返回值中,不要以局部变量的形式出现。
定义:一个对象应该对其他对象有最少的了解。
设计模式六大原则.xmind

设计模式种类

创建型模式——单例模式、静态工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
结构性模式——桥接模式、适配器模式、装饰器模式、代理模式、组合模式、门面模式、享元模式
行为型模式——解释器模式、模板方法模式、迭代器模式、调节者模式、备忘录模式、状态模式、策略模式、观察者模式、责任链模式、命令模式、访问者模式

工厂模式

简单工厂模式

意思是写一个工厂类,由工厂创建具体实例。

  1. ![image.png](https://cdn.nlark.com/yuque/0/2019/png/607561/1575105041114-57d8aa9d-8173-402e-924f-d325bcc7b42d.png#align=left&display=inline&height=264&name=image.png&originHeight=450&originWidth=627&size=20358&status=done&style=none&width=368)

缺陷:
不符合单一原则:一个类负责创建对应实例的创建
不符合开闭原则:拓展不同实例的时候,需要修改已有的代码

工厂方法模式

解决简单工厂模式的缺陷,拓展新种类代码的时候,不需要修改已有的代码

意思是用户需要不同的种类的产品的创建,则从对应产品实例化的工厂创建。
举例:
编写一个工厂接口类,然后根据需求不同种类的产品编写不同的具体工厂类(实现该抽象类)。
在引用的时候,则可以使用多态方式获取对应工厂,然后使用该工厂创建对应的产品实体。

        ![image.png](https://cdn.nlark.com/yuque/0/2019/png/607561/1575105381998-b0f4e5ee-1708-4ba6-b4bf-7e7287892933.png#align=left&display=inline&height=285&name=image.png&originHeight=416&originWidth=665&size=16377&status=done&style=none&width=456)

抽象工厂模式

为了应对产品方面的考虑,增加了抽象工厂概念。
比如:奔驰贴上了宝马的商标这种事情是不对的,所以需要用抽象进行约束

在工厂方法上优化,在抽象类上添加对应的抽象方法或抽象成员对象,这是为了应对防止产品之间具备一定的联系,用抽象的方式让这些产品联系起来。

image.png

工厂之间的区别
  简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
  工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
  抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)

注意事项
  (1)工厂类常常采用单例模式(Singleton)。
  (2)工厂类拥有基类(定义共同接口),基类可以为纯虚类,也可以定义缺省方法。
  (3)对于工厂方法和抽象工厂,基类中的生产产品的函数常常为虚函数,以实现动态绑定。
  (4)调用工厂方法的函数通常采用工厂实现的指针和引用作为形参,以便根据不同的工厂实参调用不同的工厂方法。

建造者模式

定义:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。
听听上面官方给的定义,这是人讲的话吗??
通俗理解:用户想要一辆车,车在工厂中创建过程是标准并且复杂的,而将从获取零件组装制造成一辆车最后交付给用户这一过程,就叫做建造者模式。

建造者模式中包含指挥者/建造者/抽象建造者/产品实体类,指挥者指挥建造者建造这个产品的过程顺序,建造者负责创建。(符合单一值则原则),但为了保证整个工艺标准,需要做一个约束,这时候需要给建造者做一个抽象来保证这个工艺的完整标准

//产品类
public class Bike { 

    private IFrame frame; 
    private ISeat seat; 
    private ITire tire; 

    public IFrame getFrame() { 
        return frame; 
    } 
    public void setFrame(IFrame frame) { 
        this.frame = frame; 
    } 
    public ISeat getSeat() { 
        return seat; 
    } 
    public void setSeat(ISeat seat) { 
        this.seat = seat; 
    } 
    public ITire getTire() { 
        return tire; 
    } 
    public void setTire(ITire tire) { 
        this.tire = tire; 
    } 
}
// 抽象 builder 类 
public abstract class Builder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
}
// 具体 builder 类 
public class MobikeBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new AlloyFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new DermisSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new SolidTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

public class OfoBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new CarbonFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new RubberSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new InflateTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
}
//指挥者类
public class Director { 
    private Builder mBuilder = null; 
    public Director(Builder builder) { 
        mBuilder = builder; 
    } 
    public Bike construct() { 
        mBuilder.buildFrame(); 
        mBuilder.buildSeat(); 
        mBuilder.buildTire(); 
        return mBuilder.createBike(); 
    } 
}
//用户获取产品的过程
public class Click { 
    public static void main(String[] args) { 
        showBike(new OfoBuilder()); 
        showBike(new MobikeBuilder()); 
    } 
    private void showBike(Builder builder) {
        //用户只需要告诉指挥者想要哪种型号的产品
        Director director = new Director(builder); 
        //用户在这里就能得到产品,并且产品的质量标准有保证
        Bike bike = director.construct(); 
        bike.getFrame().frame(); 
        bike.getSeat().seat(); 
        bike.getTire().tire(); 
    } 
}

代理模式

代理模式的由来

需求
v0.0实现增删查功能
v0.1在0.0的功能上增加日志打印
v0.2在0.1的日志打印上,修改成英文日志打印
v0.3增加修改功能
v0.4凌晨0-6点,不打印日志

问题——从上面的版本变更会产生一些问题
1、0.1开始,代码在重复
2、0.2版本,需要修改很多地方
3、代码急剧膨胀,核心代码与非核心代码

对于这些问题,问题不能完全解决,或者具有很多不太合适的解决办法,这时就有了动态代理模式

理解代理模式

假设有这么个场景,你们是一个软件公司,客户需要对程序员开发一个软件,显然不会带着需求直接找开发谈,而是会去找商务去谈,这个时候客户就会认为商务就代表了这个公司。

image.png

从上图可以看出代理对象和真实对象存在代理和被代理的关系,那代理模式有什么作用呢?
代理模式的作用就是在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制调用者访问真实对象。
所以得出代理必须分为两个步骤:

  - 代理对象和真实对象建立代理关系
  - 实现代理对象的逻辑方法(本文使用的是JDK中的动态代理技术)

使用java完成动态代理模式

先看一个图理解JVM加载类
image.png

从上图可以得出,字节码有两种途径获得,一种是硬盘中的.java文件由javac编译出来,另一种是程序运行期时由流向内存写入字节码,而动态代理则是由运行期创建字节码文件的方式创建代理对象,那如何获取代理对象?

jdk内部提供Proxy.newProxyInstance方法获取代理对象,Proxy.newProxyInstance方法需要三个参数(类加载器,接口字节码数组,InvocationHandler接口实现类)

1、获取类加载器
通过类加载器加载字节码文件到内存中,再进一步实例化对象(简单来说就是,只要涉及实例化类的对象,就一定要加载类的字节码,而加载字节码就必须使用类加载器)

2、获取需要代理的接口数组,将所有需要代理的接口装进一个数组传入
生成的代理对象会自动实现这些接口的方法

3、获取InvocationHandler接口的实现类

最后调用代理对象的方法最终都会执行InvocationHandler实现类里的invoke方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface ICalc{
    //ICalc接口

    int add(int a,int b);
    int sub(int a,int b);
    int mul(int a,int b);
    int div(int a,int b);
}

class ICalImpl implements ICalc{
    //ICals实现类

    @Override
    public int add(int a, int b) {
        int result = a + b;
        return result;
    }

    @Override
    public int sub(int a, int b) {
        int result = a - b;
        return result;
    }

    @Override
    public int mul(int a, int b) {
        int result = a * b;
        return result;
    }

    @Override
    public int div(int a, int b) {
        int result = a / b;
        return result;
    }
}

class ProxyExample implements InvocationHandler{
    //代理类

    private Object target;

    public ProxyExample() {}

    public Object bind(Object target){
        this.target = target;

        //Proxy.newProxyInstance三个参数
        //1、类加载器
        //2、接口字节码数组
        //3、InvocationHandler接口实现类
        //对代理对象的调用不会进入真正的实现方法中,而是进入第三个参数的Invoke方法中
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理对象执行的所有方法最终都会在这里执行

        //Object proxy这个参数代表代理对象本身
        //Method method这个参数代表调用的方法
        //Object[] args这个数组代表传入的参数

        //利用反射的方式调用对象
        Object result = method.invoke(target,args);

        return result;
    }
}

public class proxyTest {
    public static void main(String[] args) {

        //真实对象
        ICalc trueObject = new ICalImpl();
        System.out.println(trueObject.add(1,2));

        ProxyExample proxy = new ProxyExample();
        //获取代理对象(将真实对象和代理对象进行绑定)
        ICalc proxyObject = (ICalc) proxy.bind(trueObject);

                //proxyObject对象是一个代理对象,它会进入代理的逻辑方法invoke里
        System.out.println(proxyObject.add(1,1));
        System.out.println(proxyObject.div(1,1));
        System.out.println(proxyObject.mul(1,1));
        System.out.println(proxyObject.sub(1,1));
    }
}

类比前面举的例子,proxy相当于商务对象,target相当于开发人员,bind方法就是建立商务和开发人员代理关系的方法,而invoke就是商务逻辑,它将控制对开发人员的访问。

适配器模式

装饰器模式

单例模式