一文读懂设计模式

一、简介

  • 设计模式分为三大类
    • 创建型(5):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
    • 结构型(7):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
    • 行为型(11):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
  • 设计模式的七大原则
    • 单一职责原则:一个类只作用于一种处理
    • 接口隔离原则:最小实现原则
      • 当一个接口中有多个方法,实际上实现类,并不会使用全部方法,因此可以将接口拆分为多个小接口,实现类只需要实现自己需要的小接口即可
    • 开放-封闭原则(OCP):软件实体(类、模块、函数)可以拓展,但是不可修改
      • 简单来说,在一个模块上扩展新功能而不是修改现有功能
    • 依赖倒转原则(DIP):
      • 核心思想是接口开发,将实现类的依赖倒转到接口
      • 高层模块不应该依赖底层,两个都应该依赖抽象(依赖接或口者抽象类)。
      • 抽象不应该依赖细节,细节依赖抽象
      • 假设我想用一个方法来接受所有的消息(微信、邮件 、qq等)
        • 创建一个消息的总接口,其他的消息类型都实现这个接口
        • 方法的接收参数为总接口对象,任意一个子实现类都可以调用此接口实现信息的接受
      • 里氏代换原则(LSP):子类型必须能够替换掉它们的父类型
      • 迪米特法则(LoD):如果两个类不必直接通信,那么这两个类不应当发生直接的相互作用
        • 简言之:在A类中,不要出现对于B类的逻辑操作,B对象尽可以作为参数或者返回对象
      • 合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用类继承
        • 合成:在B中 new一个A对象,而不是继承或者依赖
        • 聚合:在B中定义一个A对象的变量,构造器注入进来A
  • 作用
    • 代码的重用性(避免重复代码)
    • 可读性(代码的规范性)
    • 扩展性(可以任意扩展新的功能)
    • 可靠性(扩展新功能,不会影响旧功能)
    • 降低耦合

二、各设计模式详解

2.1、 创建型

  • 工厂模式
    • 简单工厂模式
      • 定义了一个创建对象的类,由这个类来封装实例化对象的行为
        • 因为所有的类型定义都在工厂端,因此扩展的时候修改工厂,违背了开放封闭原则
    • 工厂方法模式
      • 定义了创建对象的接口,由子类(不同的工厂)实例化各自的对象
        • 所有工厂不能对外一致,需要创建多种实例对象
    • 抽象工厂模式
      • 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
        • 可以实例化多种子对象,直接实例化接口,强转为所需要的实现类即可
  • 单例模式
    • 确保一个类最多只有一个实例,并提供一个全局访问点
      • 饿汉式:初始化的时候直接创建
        • 静态成员变量:初始化类的时候就会加载成员变量
        • 静态代码块:初始化类的时候就会加载静态代码块
      • 懒汉式:需要用到的时候才会创建
      • 静态内部类
        • 当加载静态内部类的对象实例,是线程安全的
          • 假设sign类有一个静态内部类SignStatic
          • SignStatic中new了一个静态的sign实例对象instance
          • 在外部调用sign.SignStatic.instance 即可得到sign的实例化对象
      • 枚举
    • 多线程情况下
      • 加锁解决多线程初始化对象
        • 对象加Volatile避免指令重排序
        • 反射机制调用构造方法会重复创建对象,new Instance()方法底层,判断了枚举对象,如果是枚举对象则会抛出异常,禁止反射构造对象
  • 生成器模式
    • 封装一个复杂对象构造过程,并允许按步骤构造。
      • 简单理解:构建一个接口,封装了一个流程的完整步骤(5个方法),分别创建实现类实现方法,完成实际的业务
        • 调用方,只需要调用一个方法(方法内部按顺序调用了整个流程的方法)
      • Appendable接口定义了append方法,AbastractStringBuilder实现了Appendable, StringBuilder继承了AbastractStringBuilder
        • Appendable就是底层完成任务的接口,AbastractStringBuilder就是具体的实现类,StringBuilder就是提供给用户调用的
  • 原型模式
    • 通过复制现有实例来创建新的实例,无需知道相应类的信息
      • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向
        的还是原对象所指向的。
        • 默认的clone是浅拷贝,只会拷贝基本类型,引用类型还是只想之前的对象
      • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
        • 重写clone方法
        • 对象序列化
    • 原型模式的本质就是clone:clone会大大缩减创建对象所带来的效率问题

2.2、结构型

  • 适配器模式
    • 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口 不匹配所造成的类的兼容性问题
      • 类适配器模式:本质就是A类继承B类(B类实现B接口)并实现A接口,A类此时将拥有AB所有方法,可以任意转化
      • 对象适配器模式:A不再继承B,但是在A的实现类直接引入了A
      • 接口适配器模式:创建一个抽象类,由接口实现抽象方法,实现类不再关注不需要的方法
    • 举例:当一个请求过来,dispatch会根据请求类型找到合适的Adapter,分配不同handler处理请求,此处Adapter就是用来适配不同请求的兼容类
  • 装饰者模式
    • 动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
    • 假设每个人需要往咖啡里添加的东西不同
      • 创建一个Drink作为coffee和Decorator的父类,包含价格和添加佐料(desciption)字段
      • 每个人创建一个Decorator对象并调用super.setDesciption(“xxx”)方法,添加佐料
      • coffee中super.getDesciption(),获取到不同的佐料
  • 代理模式
    • 避免直接访问对象,访问对象的代理类
      • 创建代理类,代理类中引入被代理类,调用代理类,通过代理类调用被代理类的方法
      • 静态代理
        • 通过接口,代理真正的实现类,引入接口的实例操作实现类的方法
      • 动态代理
        • 不需要手动写代理接口,通过invoke代理不同的类
      • JDK动态代理:底层继承了Proxy代理类,因此只能代理接口
      • CGLIB代理
        • 动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法
        • 子类使用拦截器拦截所有父类方法的调用,相对于JDK代理和反射,效率高一点
        • 使用字节码处理框架ASM,来转换字节码并生成新的类。ASM要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉
        • 对于final方法,无法进行代理
        • Object result = methodProxy.invoke(object, args);
          • methodProxy:代理方法的引用
          • object:代理类的实例
          • aygs:方法参数
          • 此过程实际上就是通过object,调用methodProxy方法
  • 外观模式
    • 隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口
      • 对外暴露接口,隐藏复杂的业务逻辑
      • 拆分系统,对外系统和服务子系统,客户端只需要调用对外系统暴露的接口即可
  • 桥接模式
    • 将抽象部分与它的实现部分分离,使它们都可以独立地变化
    • 将A类注入到B类中,在A类中定义B类必须要复写的抽象方法,其他想要使用相关方法的对象只需要继承A类即可
    • 比如手机很多品牌并且都有有开机关机打电话功能
      • 创建一个功能类,创建一个手机品牌类(实现了功能类),各详细品牌继承品牌父类
      • 如果要增加手机类型,只需要创建子类继承品牌类即可
      • 如果想要增加功能,只需要在功能类添加即可,如果特殊,也可以创建功能类子类
  • 组合模式
    • 类似于创建的树状结构,部分和整体的关系
  • 享元模式(Flyweight)
    • 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用

2.3、 行为型

  • 策略模式(strategy)
    • 简单来说就是一个统一父类,定义一个方法,子类分别实现不同的业务,具体需要执行什么,有使用者决定
    • 举例:假设有一个计算方法,多个子类继承实现加减乘除方法,用户根据业务使用具体的实现类
  • 模板方法模式(Template Method)
    • 简单解释
      • 一个抽象类中,有一个主方法,再定义n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,
      • 通过调用抽象类的方法,在抽象类中调用自身定义的其他方法,从而实现对子类的调用
  • 观察者模式(Observer)
    • 简言之就是订阅通知,A发布消息通知其他的依赖对象,跟随发生对应的变化
  • 迭代子模式(Iterator)
    • 就是集合迭代器
  • 责任链模式(Chain of Responsibility)
    • 简言之就是A中调用B,B中调用C,C结束或者反过来调用A
    • 责任链:可以是一个顺序的链条、可以是环形、父子树结构
      • spring的源码:拦截器中一层一层的顺序调用拦截器
  • 命令模式(Command)
    • 将命令下达者和命令执行者解耦
      • 生产者和消费者,将消息发送到队列中,消费者接受并返回结果
      • 第三方调用、Struts请求和呈现分离技术等
  • 备忘录模式(Memento)
    • 形象点,可以被称为备注模式
  • 状态模式(State)
    • 可以通过设置不同的状态,显示不同的形态
      • 创建一个状态抽象类,构造各改状态的方法,创建状态方法的实现类,创建一个通用的状态处理类,根据不同的操作调用不同的方法,改变状态等
  • 访问者模式(Visitor)
    • 简言之:目前有一个对象,很多人都需要去访问让他,获取自身需要的信息
      • 创建一个Subject对象,对象里有一个接受访问者的方法,方法里反过来调用接收者的方法,并带着特定的参数
      • new 访问者,new 一个共有的Subject,Subject调用自身的方法,将访问者传递进去
  • 中介者模式(Mediator)
    • 比如我们要启动100各机器,我们不可能一个一个启动,需要把他们放在一起统一管理,因此,需要新增了中介对象,中介对象中注入了所有对象,同时启动或者关闭,或者特殊的方法
  • 解释器模式(Interpreter)
    • 一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。

三、经典面试题

  • 使用UML类图画出原型模式的核心角色
  • 原型模式的深拷贝和浅拷贝,并写出深拷贝的两种方式的源码(重写clone方法和使用序列化)
    • 重写clone方法:
      • 如果当前类有引用类型,可以复写clone方法
      • 基本数据类型默认的clone方法也是深拷贝
      package com.grea.qz.dao;
      import com.grea.qz.model.modules.Order;
      import java.io.*;
      public class DeapClone implements Serializable, Cloneable {
          private String name;
          private Order order;
          private int age;
          //方式1   复写clone方法
          @Override
          protected Object clone() throws CloneNotSupportedException {
              //先调用父类clone方法,clone一下基本数据类型(name和age)
              DeapClone deapClone = (DeapClone) super.clone();
              //调用引用类型(Order)的clone方法,clone引用类型
              //Order类需要复写clone方法
              deapClone.order = (Order) order.clone();
              return deapClone;
          }
      
          //方式2   序列化
          public Object deapClone() {
              ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
              ObjectOutputStream objectOutputStream = null;
              ByteArrayInputStream byteArrayInputStream = null;
              ObjectInputStream objectInputStream = null;
              try {
                  //序列化对象,先将当前对象写出
                  objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                  objectOutputStream.writeObject(this);
                  //反序列化,重新写入新对象
                  byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                  objectInputStream = new ObjectInputStream(byteArrayInputStream);
                  DeapClone deapClone = (DeapClone) objectInputStream.readObject();
                  return deapClone;
              } catch (Exception e) {
                  return null;
              }finally {
                  try {
                      byteArrayOutputStream.close();
                      byteArrayInputStream.close();
                      objectOutputStream.close();
                      objectInputStream.close();
                  } catch (IOException e) {
                      return null;
                  }
              }
          }
      }
      
      
    • 使用序列化
      • 本质就是先将对象写出,然后重新写入
  • 源码分析spring中的原型模式
    • 在beans.xml文件中 配置bean对象
    • 应用程序启动的是否,会去加载xml文件,注册bean对象
    • 在getBean(String name)方法中,会执行doGetBean方法,其中会判断bean的对象模式(单例、原型)
  • spring源码中用到的设计模式
    • 工厂模式:beanFactory以及ApplicationContext创建中
    • 模板模式:某一类中会存在空方法,子类继承后实现这些空方法实现自定义内容,此方式,称之为模板模式
    • 代理模式:AOP 动态代理
    • 策略模式:继承父类,各子类实现各自功能,比如
    • 单例模式:各种的单例bean
    • 观察者模式:各种event
    • 适配者模式:xxxAdapter
    • 装饰者模式:xxxWraper或者Decorator

你可能感兴趣的:(java,设计模式)