Skip to content

Latest commit

 

History

History
1111 lines (714 loc) · 27.9 KB

设计模式.md

File metadata and controls

1111 lines (714 loc) · 27.9 KB

类与类之间的关系

1.依赖

一个类作为另外一个类的实例对象的方法的参数

public class Oil{
    private String type;
}

public class Car{
    // 汽车依赖汽油
    public void beforeRun(Oil oil)
}

2. 继承(特殊的依赖关系)

继承抽象类

3. 实现(特殊的依赖关系)

实现接口

4. 关联(特殊的依赖关系)

司机和汽车的关系。汽车可以有很多司机,司机也可以开很多汽车。

5. 组合

整体与部分,并且整体与部分有一致的生命周期

人和头部、身体的关系。生命周期一致

6. 聚合

整体与部分,但整体与部分有各自的生命周期。

公交车司机和工作服、工作帽的关系 。司机可以更换工作服、工作帽

强弱排队

继承 > 实现 > 组合 > 聚合 > 关联 > 依赖

1.设计模式七大原则

  • 单一职责原则

控制类的粒度大小。

一个对象只包含单一的职责,而且该职责完全的被封装在一个类中。

  • 开闭原则

对扩展开放,对修改关闭。尽量在不修改源代码的前提下进行扩展。

  • 里氏代换原则

所有的基类对象被子类替换,程序将不会受到影响。这是实现开闭原则的重要方式之一

  • 依赖倒转原则

高层模式不依赖于底层模块,都依赖于抽象,抽象不依赖于细节,细节依赖于抽象

  • 接口隔离原则

与接口单一原则相辅相成。该类实现的接口中不要存在不属于它的方法。

  • 合成复用原则

尽量使用对象组合或者对象聚合的方式实现代码复用

  • 迪米特法则

2.设计模式分类

  • 根据目的分类

根据**目的(用来做什么)**分类分为创建型、结构型、行为型3类

  • 根据范围分类

根据**范围(该模式用于处理类之间的关系还是处理对象之间的关系)**可以分为类模式和对象模式

范围目的 创建者模式 结构性模式 行为型模式
类模式 工厂方法模式 (类)适配器模式 解释器模式 模板方法模式
对象模式 抽象工厂模式 建造者模式 原型模式 单例模式 (对象)适配器模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式 职责链模式 命令模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 访问者模式

image-20210415230523290

创建者模式

1.简答工厂模式

模式分三个角色:

  • Factory(工厂角色):工厂类负责创建具体产品类,并且在创建时可以直接进行一些其他的操作,比如初始化。
  • Product(抽象产品角色):抽象产品是所有具体产品类的父类,具体产品类只需要实现这个接口,并重写里面的方法。
  • ConcreteProduct(具体产品角色):具体产品类是具体实现产品的方法的类。

一个工厂负责生产所有的产品

缺点:

  • 集中了所有的产品创建逻辑,职责过重,一旦不能正常工作,整个系统都会受影响
  • 系统扩展困难,一旦添加新产品不得不修改工厂类逻辑。
  • 简单模式使用了静态工厂方法,造成工厂角色无法继承。

2.工厂模式

模式分四个角色:

  • 抽象产品
  • 具体产品
  • 抽象工厂
  • 具体产品工厂

一个具体工厂只负责生产一种产品。会导致类的数量爆炸增长

缺点:

  • 增加新产品时需要写增加具体的工厂类,类的个数将大量增加
  • 为了系统可扩展,加入抽象层,使系统的变复杂

3 抽象工厂模式

产品等级结构:产品的继承结构,例如一个抽象类是电视剧,子类可以是海尔电视机、TCL电视机等。抽象类和具体类之间构成产品等级。

产品族:由同一工厂生产的位于不同的产品等级结构中一组产品。例如海尔工厂生产海尔电视机和电冰箱。他们构成了一个产品族。

抽象工厂模式角色:

  • 抽象工厂
  • 具体工厂
  • 抽象产品
  • 具体产品

一个具体工厂负责生产一个产品族的产品。

缺点:

  • 增加新产品等级结构比较麻烦,需要对系统有较大的改动,甚至需要修改抽象类的代码

建造者模式

  • 产品
  • 抽象建造者
  • 具体建造者
  • 指挥类

通过指挥方法指挥具体建造者可以创建出不同的产品

有时候会把指挥类和抽象建造者结合,把指挥类中的建造产品的过程放在抽象建造者中

使用场景

  • 相同的方法,不同的执行顺序产生不同的结构
  • 多个部件或者零件都可以装配到一个对象中,但是产生的结果又不相同
  • 产品类非常复杂,产品类中的不同调用顺序产生不同的结果
  • 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值

建造者模式和工厂模式的区别

自己:

  • 工厂模式关注的是生产的类,我是什么工厂我就生产什么样的类。

  • 建造者模式不仅要生产产品,还关注生产什么样的产品。通过构建者可以设置生产的产品。

别人:

  • 建造者更加注重方法的调用,工厂注重创建对象。
  • 创建对象力度不同,建造者创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
  • 关注重点不一样,工厂模式只需要把对象创建出来就行,建造者不仅要创建对象,还要知道创建什么样的对象
  • 建造者在建造过程的不一样,最终的结果组成也不一样。

单例模式(饿汉式)

1.

public class EagerSingleton {

    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton(){
    }

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

2.静态代码块(饿汉式)

public class EagerSingleton {

    private static final EagerSingleton instance;

    private EagerSingleton(){}

    static{
        instance = new EagerSingleton();
    }
    
    public static EagerSingleton getInstance(){
        return instance;
    }
}

饿汉式的缺点:如果全称没有用到,产生的对象占内存

3.懒汉式(线程不安全)

package eagersingleton;

public class EagerSingleton {

    private static EagerSingleton instance;

    private EagerSingleton(){}
    
    public static EagerSingleton getInstance(){
        if (instance == null){
            instance = new EagerSingleton();
        }
        return instance;
    }
}

4.锁机制

同步代码块或者synchronized锁机制。

package eagersingleton;

public class EagerSingleton {

    private static EagerSingleton instance;

    private EagerSingleton(){}

    synchronized public static EagerSingleton getInstance(){
        if (instance == null){
            instance = new EagerSingleton();
        }
        return instance;
    }
}

这种方式效率太低

5.双重锁机制

package eagersingleton;

public class EagerSingleton {

    private static EagerSingleton instance;

    private EagerSingleton(){}

    synchronized public static EagerSingleton getInstance(){
        if (instance == null){
            synchronized (EagerSingleton.class){
                if (instance == null){
                    instance = new EagerSingleton();
                }
            }
        }
        return instance;
    }
}

6.静态内部类

package eagersingleton;

public class EagerSingleton {
    
    private static class singletonInstance{
        private static final EagerSingleton instance = new EagerSingleton();
    } 
    
    public static EagerSingleton getInstance(){
       return singletonInstance.instance;
    }
}
  • 采用了类装在机制保证初始化实例只有一个线程。
  • 静态内部类只有在只有在被调用的时候才会加载,从而实现懒加载
  • 类的静态属性只有在第一次加载的时候初始化,保证了线程安全。

7.枚举

package eagersingleton;

public enum EagerSingleton01 {
    INSTANCE;

    private Object connection = null;

    private EagerSingleton01() {
        connection = new Object();
    }

    public Object getConnection() {
        return connection;
    }
}

///////////////////////////////////////////////
Object connection = EagerSingleton01.INSTANCE.getConnection();
Object connection1 = EagerSingleton01.INSTANCE.getConnection();

// true
System.out.println(connection == connection1);

防止反射破坏单例

public class EagerSingleton {

    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton(){
        if (instance != null){
         // 抛异常,不允许创建
        }
    }

    public static EagerSingleton getInstance(){
        return instance;
    }
    
    private Object readResolve(){
    return instance;
}

序列化破坏单例

// 在序列化的类中加上下面的方法
public class EagerSingleton {

    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton(){
    }

    public static EagerSingleton getInstance(){
        return instance;
    }
    
    private Object readResolve(){
    return instance;
}

原型模式

浅拷贝

package shallowcopy;

public class Address {
    private String name;

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

    public String getName() {
        return name;
    }

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

}

//////////////////////////////////////////
package shallowcopy;

public class Sheep implements Cloneable{

    private String name;

    private int age;

    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {

        Sheep sheep = null;

        sheep = (Sheep) super.clone();

        return sheep;
    }
}


//////////////////////////////////////////////////
package shallowcopy;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("小样", 18);
        sheep.setAddress(new Address("1111"));
        Sheep clone = (Sheep)sheep.clone();
        System.out.println("原生没变之前的"+clone.getAddress().getName());
        sheep.getAddress().setName("2222");
        System.out.println("原生改变之后的"+clone.getAddress().getName());
    }
}

基本数据类型会直接值传递。引用类型传递会直接把引用传递。不会把引用指向的内容复制。这样在其中一个对象中修改引用类型的属性会影响其他的。

深拷贝

重写clone方法实现深拷贝。对引用类型属性单独clone

package deepcopy;

public class Address implements Cloneable{
    private String name;

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

    public String getName() {
        return name;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

/////////////////////////////////////////////////////////
package deepcopy;

public class Sheep implements Cloneable{

    private String name;

    private int age;

    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {

        Sheep sheep = null;

        sheep = (Sheep) super.clone();

        sheep.address = (Address) address.clone();

        return sheep;
    }
}

////////////////////////////////////////////////////////////
package deepcopy;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("小样", 18);
        sheep.setAddress(new Address("1111"));
        Sheep clone = (Sheep)sheep.clone();
        System.out.println("原生没变之前的"+clone.getAddress().getName());
        sheep.getAddress().setName("2222");
        System.out.println("原生改变之后的"+clone.getAddress().getName());
    }
}

image-20220112112331968

通过对象序列化

package deepcopy;

import java.io.Serializable;

public class Address implements Serializable {
    private String name;

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

    public String getName() {
        return name;
    }

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

////////////////////////////////////
package deepcopy;

import java.io.*;

public class Sheep implements Serializable {

    private String name;

    private int age;

    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Object deepClone() {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try{
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            Sheep o = (Sheep)ois.readObject();
            return o;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

///////////////////////////////////////////
package deepcopy;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("小样", 18);
        sheep.setAddress(new Address("1111"));
        Sheep clone = (Sheep)sheep.deepClone();

        System.out.println("原生没变之前的"+clone.getAddress().getName());
        sheep.getAddress().setName("2222");
        System.out.println("原生改变之后的"+clone.getAddress().getName());
    }
}

image-20220112113823173

结构型设计模式

适配器模式

1.类适配器

public class AC220{
    public int outputAC220V(){
        int output = 220;
        System.out.printIn("输出电压"+output+"V");
        return output;
    }
}

//////////////////////////////////////////////////////
public interface DC5{
    int output5V();
}

/////////////////////////////////////////////////////
public class PowerAdapter extends AC220 implements DC5{
    public int output5V(){
        int adapterInput = super.putputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.printIn("输出电压"+adapterOutput+"V");
        return adapterOutput;
    }
}

适配器和适配者之间是继承关系

2.对象适配器

public class AC220{
    public int outputAC220V(){
        int output = 220;
        System.out.printIn("输出电压"+output+"V");
        return output;
    }
}

//////////////////////////////////////////////////////
public interface DC5{
    int output5V();
}

/////////////////////////////////////////////////////
public class PowerAdapter implements DC5{
    
    private AC220V ac220;
    
    public PowerAdapter(AC220V	ac220){
        this.ac220 = ac220
    }
    
    public int output5V(){
        int adapterInput = super.putputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.printIn("输出电压"+adapterOutput+"V");
        return adapterOutput;
    }
}

适配者是适配器的属性。不再是继承关系

3.接口适配器

如果接口的方法过多,直接实现接口就会很多空方法,显示臃肿,而此时我们可以使用抽象类实现接口,在抽象类中实现空方法,有用的方法定义成抽象方法。适配器直接继承抽象类

4.双向适配器

装饰器模式

装饰器模式是一种特殊静态代理的模式。装饰器模式强调的是自身的功能加强。代理模式强调过程控制。Proxy完全掌握对RealSubject的访问控制,代理类可以对被代理类进行功能扩展、功能缩减甚至功能散失。

应用场景

当需要对类进行自身的功能加强时可以采用装饰器

代理模式

代理模式可以在不更改源码的情况下进行扩展

1.静态代理

  • 接口:代理类和被代理类都实现
  • 代理类:在该类中对被代理类进行扩展
  • 被代理类:原项目中的类,就是要进行扩展的类

静态代理的缺点:对100个类进行代理,就要有100个代理类,这样就会产生很多类,因此产生了动态代理

2.动态代理-JDK

  • 接口:代理类和被代理类都实现
  • 代理类(Proxy):用java自带的Proxy动态创建代理类
  • 被代理类:原项目中的类,就是要进行扩展的类
public static Object newProxyInstance(ClassLoader loader,
                                      <?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
//返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。 

//loader - 类加载器来定义代理类 
//interfaces - 代理类实现的接口列表 
//h - 调度方法调用的调用处理函数   手动写一个实现InvocationHandler接口的类
    
Search  = (Search) Proxy.newProxyInstance(realSearch.getClass().getClassLoader(), realSearch.getClass().getInterfaces(), new Hander(realSearch);
        

要手动实现一个InvocationHandler 接口的实现类,用以Proxy创建对象的时候的参数用

package cn.runningone;

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

public class Hander implements InvocationHandler {

    private Object target;

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

    public void setTarget(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        verify();
        Object result = method.invoke(target, args);
        log();
        return result;
    }

    public void verify(){
        System.out.println("动态代理-身份验证成功");
    }

    public void log(){
        System.out.println("动态代理-已经进行了日志记录");
    }
}

动态代理和静态代理对比

如果需要代理不同的真实类或者代理真实类的不同方法,动态代理的优势很大。

被代理类如果增加了新的方法,代理类需要同步增加,违反了开闭原则。但是动态代理在运行时生成代码,取消了对被代理类的限制。

动态代理要对目标增强逻辑进行扩展,结合策略模式,新增策略类即可。

3.动态代理-Cglib

动态代理只要给被代理类扩展一样的内容就可以用同一个InvocationHandler接口的实现类,但是如果被代理类扩展内容不一样需要重新写一个InvocationHandler接口的实现类

应用场景

当我们需要对一些类进行统一的功能加强时可以采用代理类。

门面模式(外观模式)

有时候需要一个动态需要经过一系列的过程。我们可以通过一个门面类来封装这些方法,对面只暴露一个接口。

util类是门面模式

对一些复杂的功能封装提供对外一个简洁的接口。

享元模式

经常用在池化技术上。当相似度很高的类经常使用时可以保存在享元工厂中

  • 抽象享元类
  • 具体享元类
  • 享元工厂类
public class FlyweightFactory{
    // 保存抽象享元类的子类
    // 集合相当于缓存作用。
    Map<String, Object> maps = new HashMap<>();
    
    // 如果有就直接从集合中返回,没有就创建放到集合中
    public Object getObject(String name){
        if(!maps.containsKey(name)){
            Object object = new Object();
            maps.put(object);
        }
        return maps.get(name);
    }   
}

内部信息储存在对象的内部,不随着环境的改变而改变。

外部信息会随着环境的改变而改变。

组合模式

定义一个抽象构件类,既可以代表叶子,也可以代表容器。无需知道是叶子还是容器,我们都对它进行统一处理。

抽象构件类

叶子类:真正的业务处理类。

容器类:里面可以包含容器或者叶子

1.透明组合

叶子类和容器类本身是有区别的。抽象构件类中定义全部的方法。但是有一些方法是容器独有的,这样叶子类继承了构件类也可以使用这些方法。

image-20220110112909886

2.安全组合

叶子类和容器类本身是有区别的。抽象构件类中定义两者都需要的方法。在容器类中单独定义自己需要的方法。这样叶子类就调用不到不属于自己的方法。

image-20220110112954838

桥接模式

当有两种东西需要搭配的时候采用组合模式。比如支付的时候的支付方式和验证密码方式 两个需要搭配

行为型设计模式

委派模式

委派模式的形式和静态代理是一样的

委派角色同意管理不同的类,然后交给不用的类。

在内部类定义一个map类型的属性,通过该类的某个方法传来的参数去判断调用map里保存的类

springmvc的DispatcherServlet

// 经理类
public class DispatcherServlet extends FrameworkServlet {
    
    // 内部定义的能调用的类的集合(员工的集合)
    private Map<String,Method> handlerMapping = new Map<String,Method>();
    
    private void doDispatch(HttpServletRequest request, HttpServletResponse response){
        String url = request.getRequestURI();
        // 通过判断参数来选择集合中的那个类来处理
        handlerMapping.get(url);
    }
}

策略模式

可以消除if else

责任链模式

可以消除if else

角色分类

抽象策略:一般是接口

具体策略:具体策略

对某一种逻辑有好几种方式,例如支付场景可以有多种支付方式

一般与委派模式结合使用,把所有的策略类封装在委派模式的委派类的属性集合中。通过不同方式调用不同的策略。

模板方法

角色分类

  • 抽象模板:一般定义为抽象类(定义一套算法框架/流程)
  • 具体实现:(对算法框架的某些具体流程进行实现)

把一样的东西抽象在抽象类中,把不一样的东西留给子类实现。提高代码的复用性。

把不同的算法逻辑分离到不同子类中,扩展子类实现增加新的行为。

把公共的代码封装在抽象父类中,然后在子类中实现父类的抽象方法。在使用的时候直接new具体的子类

JDK的AbstractList就是抽象模板,AbstractList的子类就是具体实现。 AbstractList的子类中实现get方法。

在使用的时候也直接new子类

观察者模式

类似发布-订阅模式。当一个对象的行为变化影响另外一个对象的行为时可以用观察者模式

状态模式

可以消除if else

责任链模式

最常用的就是过滤器。

一些相同的对象维持成一个链

变现有两种形式

在当前对象中就保留对下一级别的引用

用一个chain的类专门来保存这些对象。通过List保存对象,然后一级级的调用。

FilterChain专门保存Filter对象,FilterChain让Filter形成链表

中介者模式

形式与静态代理模式一样,在中介者类中保存了同事类的属性。也和委派模式有点像。

把两个业务类之间的依赖全部都转为和中介者的依赖。

备忘录模式

需要把某个对象的状态保存下来的时候用这个

  • 原发器

  • 备忘录:用于储存原发器类内部的状态

  • 负责人:保存备忘录,但是不能对备忘录进行修改

使用场景

对撤销功能可以采用备忘录模式

访问者模式

不同的访问者对应不用的结果

  • 抽象访问者
  • 具体访问者:
  • 抽象元素
  • 具体元素:
  • 结构对象:保存所有的元素对象
// 抽象元素
public interface IElement{
    void accept(IVisitor visitor);
    void operation();
}

// 具体元素
public class ConcreteElement implements IElement{
    public void accept(IVisitor visitor){
        visitor.visit(this);
    }
}

// 抽象观察者
public interface IVisitor{
    void visitor(ConcreteElement element);
}

// 具体观察者
public interface IVisitor{
    void visitor(ConcreteElement element){
        element.operation();
    }
}

// 结构对象
// 通过在这和组合模式结合
public class ObjectStructure{
    // 在这设立组合对象的叶子或者容器
    private List<IElement> list =  new ArrayList<>();
    
    public void accept(IVisitor visitor){
        for(IElement element:this.list){
            element.accept(visitor);
        }
    }
}

不同访问者拿到的元素所得出的结果不一样。