美文网首页
设计模式总结

设计模式总结

作者: 925781609 | 来源:发表于2019-02-05 10:32 被阅读0次

    前言:

    设计模式是用来解决复杂问题的,借助前人经验,把复杂问题变得简单,而不要生搬硬套,为了使用设计模式而使用设计模式

    经典的设计模式一共有23种,可分为创建型、结构型和行为型三大类。一些设计模式之间看起来很相似,但是目的有所不同,可以借此来区分。下文主要对一些常见的设计模式进行总结。
    具体代码请参考:
    https://github.com/925781609/pattern

    创建型:

    1. 简单工厂模式

      特点: 一个工厂里所有子类都可以创建,根据传入参数选择创建哪个

      UML图:


      简单工厂模式

    ​ 代码示例:

    //根据type,选择创建HpMouse还是DellMouse, HpMouse和DellMouse时Mouse的子类
    public class MouseFactory{
        public static Mouse createMouse(String type) throws IllegalArgumentException{
            switch(type){
                case "Hp":
                    return new HpMouse();
                case "Dell":
                    return new DellMouse();
            }
             throws new IllegalArgumentException("the type not found");
        }
    } 
    
    2. 工厂方法模式

    ​ 特点:每个子类,由对应的工厂创建创建

    ​ UML图:


    工厂方法模式

    代码示例:

    //  HpMouse由HpMouseFactory创建, DellMouse由DellMouseFactory创建
    public class HpMouseFactory implements MouseFactoy{
    
        @Override
        public Mouse createMouse(){
            return new HpMouse();
        }
    }
    
    public class DellMouseFactory implements MouseFactory{
    
        @Override
        public Mouse createMouse(){
            return new DellMouse();
        }
    }
    
    1. 抽象工厂模式

      当工厂模式创建多个对象时, 工厂模式即变为抽象工厂模式, 原来只创建mouse, 现在创建mouse和keyboard

      在增加子类对象数量和种类时,只需要增加即可,不需要修改原有的逻辑

      UML图:


      抽象工厂模式
      4.建造者模式

      ​ 建造者模式按照顺序创建复杂对象(把内部的创建过程和细节隐藏起来)

      ​ UML图:

    建造者模式
    ​ 代码示例: 详细的参考https://github.com/925781609/pattern/tree/master/src/main/java/pattern/builder
    ```source-java
    // 具体产品,建造后对象,即为Product
    public class Computer{
    
        private List<String> parts=new ArrayList<>();
    
        public void Add(String part){
            parts.add(part);
        }
    
        public void show() {
            for (String e : parts) {
                System.out.println(e);
            }
        }
    
    }
    
    // 声明具体建造者的公共接口
    public interface Builder{
    
        public void BuildCPU();
    
        public void BuildMainBoard();
    
        public void BuildHD();
    
        public Computer getComputer();
    
    }
    
    // 具体的建造者
    public class ConcreteBuilder implements Builder{
    
        Computer computer=new Computer();
    
        @Override
        public void BuildCPU(){
            computer.Add("组装CPU");
        }
    
        @Override
        public void BuildMainBoard(){
            computer.Add("组装主板");
        }
    
        @Override
        public void BuildHD(){
            computer.Add("组装硬盘");
        }
    
        @Override
        public Computer getComputer(){
            return computer;
        }
    }
    
    // 负责控制产品对象的生产工程
    public class Director{
    
        public void construct(Builder builder){
            builder.BuildMainBoard();
            builder.BuildCPU();
            builder.BuildHD();
        }
    }
    
    // 建造者使用示例
    public class Client {
    
      public static void main(String[] args) {
        Director director = new Director();
        Builder builder = new ConcreteBuilder();
    
        // 开始建造对象
        director.construct(builder);
    
        // 获得建造的对象
        Computer computer = builder.getComputer();
    
        computer.show();
      }
    }
    ```
    
    5.单例模式

    ​ 保证一个类仅有一个实例, 并提供一个访问它的全局访问点

    ​ 应用场景: Listener本身单例, 日历Calander, IoC容器, 配置信息Config

    ​ 技术方案:保证运行过程中只有一份

    ​ 注意事项: 单例模式注意构造函数 定义为private,防止外部调用 ,此外极端情况下还需要考虑克隆、反序列化、反射对单例的破坏,可以参考:https://zhuanlan.zhihu.com/p/28491630

    ​ 技术实现方案包括: (此外还有枚举法)

    1. 饿汉式:

      在类加载的时候立即初始化, 并且创建单例对象

      优点: 没有任何的锁, 执行效率高, 绝对线程安全(在线程还没有出现之前就已经实例化了)

      缺点: 类架子的时候就初始化, 不管是否使用都占用空间

      代码示例:

      public class Singleton {
      
        private static Singleton singleton = new Singleton();
      
        // 私有构造方法,防止被实例化
        private Singleton() {
        }
      
        public static Singleton getInstance() {
          return singleton;
        }
      }
      
      
    2. 懒汉式: 默认不实例化, 只有使用的时候才实例化, 又可以分为静态内部类、双重锁检查、登记式

      静态内部类: 内部类只有在外部类被调用时,才会被加载,内部类在方法 调用前被初始化

     public class Singleton {
    
       // 私有构造方法,防止被实例化
       private Singleton() {
       }
    
       private static class SingletonHolder {
         private static final Singleton INSTANCE = new Singleton();
       }
    
       private static final Singleton getInstance() {
         return SingletonHolder.INSTANCE;
       }
     }
    

    双重锁检查:

    public class Singleton {
    
      // 对singleton对象使用volatile关键字进行限制,保证其对所有线程的可见性,并且禁止对其进行指令重排序优化。
      private static volatile Singleton singleton = null;
    
      private Singleton() {
      }
    
      public static Singleton getSingleton() {
        if (singleton == null) {
          synchronized (Singleton.class) {
            if (singleton == null) {
              singleton = new Singleton();
            }
          }
        }
        return singleton;
      }
    }
    

    注册登记式(spring种常用,待补充)

    1. 原型模式

    结构型模式:

    1.代理模式

    技术手段: jdk proxy,cglib(字节码重组框架),AspectJ(spring), asm

    代理角色通常会持有被代理角色对象的引用,以便代理角色完成工作之前或之后找到被代理对象,能够通知被代理对象

    代理分静态代理和动态代理:

    1. 静态代理:

      有目标类的引用,但是类型固定(为目标类), 否则无法使用目标类的方法

      需要定义接口或者父类,代理对象与被代理对象一起实现相同的接口或是继承相同的父类

    2. 动态代理:(通过字节码重组)

      JDK代理: 目标类要实现某一接口

      ​ 生成一个新的类,新的类实现了被代理类的所有实现。在新生成类的方法中调用目标类原来的方法

      CGLIB代理: 不需要实现相同的接口

      ​ 通过字节码重组生成新的类,继承目标类

    UML:

    代理模式

    代码实现: 关于动态代理的实现详见https://github.com/925781609/pattern/tree/master/src/main/java/pattern/proxy

    interface Subject {
    
      void request();
    }
    
    class RealSubject implements Subject {
    
      public void request() {
        System.out.println("request");
      }
    }
    
    class Proxy implements Subject {
    
      private Subject subject;
    
      public Proxy(Subject subject) {
        this.subject = subject;
      }
    
      @Override
      public void request() {
        System.out.println("PreProcess");
        subject.request();
        System.out.println("PostProcess");
      }
    }
    
    // client
    public class Client {
    
      public static void main(String[] args) {
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
      }
    }
    
    2.适配器模式

    定义一个包装类, 把适配的类(Adaptee)的api转换成目标类(Target)的api

    强调的是兼容, 不改变原来的代码也能实现新的需求, 类似于VGA-HDMI

    不想去修改老的比较稳定的系统,但是为了兼容新的需求和标准, 不得不在系统上再去做一些文章,向下兼容,

    原来的类如果不再使用, 需要打上Deprecated注解,

    1. UML图:

      1. 类适配器:继承原来的类,并且不覆写原来的方法,增加新的方法
    类适配器

    ​ 2) 对象适配器:将原来的类注入进来,调用原来的方法

    对象适配器
    1. 代码示例:

      public interface Target {
      
        //这是源类Adapteee没有的方法 
        public void Request();
      }
      
      // 源类
      public class Adaptee {
      
        public void SpecificRequest() {
          System.out.println("SpecificRequest in Adaptee");
        }
      }
      
      // 适配器类
      //适配器Adapter继承自Adaptee,同时又实现了目标(Target)接口。 
      public class Adapter extends Adaptee implements Target {
      
        //所以适配器只是将SpecificRequest()方法作了一层封装,封装成Target可以调用的Request()而已 
        @Override
        public void Request() {
          this.SpecificRequest();
        }
      }
      
    3.装饰器模式

    ​委派+适配器 为了扩展和增强,要满足is-a关系(同源同宗,也不一定有is-a关系,只要是增强就算)

    为了某个实现类在不修改原始类的基础上,进行动态地覆盖或增加方法原来的功能依旧对外开放, 依旧保留,新的功能同样也可以使用 ​

    1. 应用场景: IO流,DataSource是connection的装饰器,Spring中Detector结尾, Wrapper结尾

    2. UML 图: (原始版本比较复杂,实际都是用其简化版


      装饰器模式
    3. 代码示例:

    public interface Component {
      void sampleOperation();
    }
    
    public class ConcreteComponent implements Component {
      @Override
      public void sampleOperation() {
        System.out.println("sampleOperation method in ConcreteComponent");
      }
    }
    
    public class ConcreteDecorator implements Component {
    
      private Component component;
    
      public ConcreteDecorator(Component component) {
        this.component = component;
      }
    
      @Override
      public void sampleOperation() {
        component.sampleOperation();
        // 其他业务逻辑代码
        System.out.println("为了扩展增加的代码");
      }
    }
    

    装饰器和适配器模式的区别

    装饰器模式 是一种非常特别的适配器模式 装饰者 is-a 被装饰者 注重的是覆盖和扩展
    适配器模式 可以使用代理(has-a)或者继承(is-a)实现 注重的是兼容和转换
    4. 门面模式
    1. 应用场景
    2. UML图
    3. 代码示例

    结构型模式:

    1.策略模式

    准备一组算法,将每一个算法封装起来,让外部按需调用 , 算法彼此之间可以互换(比如打折),用户对算法实现无需了解,只需要去选择

    1. 应用场景:

      BeanFactory 根据用户配置去选择不同的beanFactory, 通常和抽象工厂模式结合使用

      比如支付方式、登录方法, 比较器(sort 方法传comparator)

    2. UML图

      image.png
    1. 代码示例

      通过在Context种传入具体的策略,避免在内部使用if-else

      public interface Strategy {
      
        double calcPrice(double booksPrice);
      
      }
      
      public class StrategyA implements Strategy {
      
        @Override
        public double calcPrice(double booksPrice) {
          return booksPrice;
        }
      
      }
      
      public class StrategB implements Strategy {
      
        @Override
        public double calcPrice(double booksPrice) {
          return booksPrice * 0.9;
        }
      
      }
      
      // context
      public class Price {
      
        //持有一个具体的策略对象
        private Strategy strategy;
      
        public Price(Strategy strategy) {
          this.strategy = strategy;
        }
      
        //计算价格
        public double quote(double booksPrice) {
          return this.strategy.calcPrice(booksPrice);
        }
      }
      
      // StrategyClinet
      public class StrategyClient {
      
        Strategy strategy = new StrategyA();
        //创建环境
        Price price = new Price(strategy);
        //计算价格
        double quote = price.quote(300);
      }
      

    基于枚举的策略模式请参考: https://github.com/925781609/pattern/tree/master/src/main/java/pattern/strategy/enumbased

    2.模板方法模式

    流程固定,某一环节有差异,将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。

    父类中定义模板方法,定义了子类方法的执行过程, 并且父类定义的模板方法不能被修改(final)

    1. 应用场景:jdbcTemplate,工作流,redisTemplate, Spring Template

    2. UML图

    模板方法模式
    1. 代码示例:

      public abstract class AbstractTemplate {
      
        // 模板方法, 定义成final, 防止子类overwrite
        final public void templateMethod() {
          // 调用基本方法(由子类实现)
          hookMethod();
          // 调用基本方法(由子类实现)
          abstractMethod();
          // 调用基本方法(已经实现)
          concreteMethod();
        }
      
        // 基本方法的声明(由子类实现,但抽象模板给出了默认实现)
        public void hookMethod() {
          System.out.println("Default hookMethod");
        }
      
        // 基本方法的声明(由子类实现)
        public abstract void abstractMethod();
      
        // 基本方法(已经实现)
        public final void concreteMethod() {
          System.out.println("AbstrateTemplate concreteMethod");
        }
      }
      
      public class ConcreteTemplate extends AbstractTemplate {
      
        @Override
        public void abstractMethod() {
          System.out.println("ConcreteTemplate abstractMethod");
        }
      
        /**
         * 不使用父类默认实现的hookMethod,自己重新定义hookMethod
         */
        @Override
        public void hookMethod() {
          System.out.println("ConcreteTemplate hookMethod");
        }
      
      }
      
      

      策略模式和模板方法模式的区别:

      策略模式只有选择权, 由用户自己选择已有的固定算法

      模板模式: 可以参与某一部分的自定义,但无法改变流程

    3.委派模式

    不是23种设计模式的一种,但是spring种比较常用: 客户请求时Boss,DispatcherServlet是委派者, Controller是被委派者

    代理+ 策略:委派模式是代理模式特殊情况(委派者要持有被委派者的引用),全权代理,通常有策略模式作干预。

    代理模式注重过程,而委派模式注重结果

    策略模式注重的是可扩展(外部扩展), 委派模式注重内部的灵活和复用

    1. 应用场景:以Delegate/Dispatcher结尾的,NIO的selector就是委派模式

    2. UML图:(这里以Spring的DispatcherServlet为例)

    委派模式
    1. 代码示例:

      // 真正干活的员工
      // @RestController
      public class UserController {
      
        // @RequestMapping(.....)
        public Object getUserById(String id) {
          return "Something";
        }
      }
      
      // 类似于项目经历分配任务
      public class DispatcherServlet {
      
        private List<Handler> handlerMapping = new LinkedList<>();
      
        public DispatcherServlet() {
          try {
            // 实际spring项目会在启动之初,扫描所有带RequestMapping注解的类,放到handlerMapping中
            Class<?> userControllerClass = UserController.class;
            handlerMapping.add(new Handler()
                .setController(userControllerClass.newInstance())
                .setMethod(userControllerClass.getMethod("getUserById", new Class[]{String.class}))
                .setUrl("/api/user"));
          } catch (Exception e) {
      
          }
        }
      
      
      public void doService(HttpServletRequest request, HttpServletResponse response) { doDispatch(request, response); }
      
      private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
      
      
      // 1、获取用户请求的URL
      String uri = request.getRequestURI();
      
      //   根据用户请求的URL,去找到这个url对应的某一个java类的方法
      
      // 2、Servlet拿到URL以后,要做权衡(要做判断,要做选择)
      // 3、通过拿到的URL去handlerMapping(可以认为是策略常量)
      Handler handle = null;
      for (Handler h : handlerMapping) {
        if (uri.equals(h.getUrl())) {
          handle = h;
          break;
        }
      }
      
      // 4、将具体的任务分发给Method(通过反射去调用其对应的方法)
      Object object = null;
      try {
        object = handle.getMethod().invoke(handle.getController(), request.getParameter("mid"));
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
      
      // 5、获取到Method执行的结果,通过Response返回出去
      // response.getWriter().write();
      
      
      
      }
      
      class Handler {
      
      
      private Object controller;
      private Method method;
      private String url;
      
      public Object getController() {
        return controller;
      }
      
      public Handler setController(Object controller) {
        this.controller = controller;
        return this;
      }
      
      public Method getMethod() {
        return method;
      }
      
      public Handler setMethod(Method method) {
        this.method = method;
        return this;
      }
      
      public String getUrl() {
        return url;
      }
      
      public Handler setUrl(String url) {
        this.url = url;
        return this;
      }
      
      
      
      }
      
      }
      
    4.观察者模式

    针对目标对象的一举一动, 要得到一个反馈,通常会和代理模式配合使用, 观察者和被观察者之间没有必然的联系,

    1. 应用场景:

    事件监听, 日志监听, Observer,Springlistener(通常会结合动态代理), 短信通知, 邮件通知

    Event是事件,EventListener, 事件的注册和监听

    Mouse观察者, Callback被观察者

    1. UML图


      观察者模式
    1. 代码示例

      public abstract class Subject {
      
        /**
         * 用来保存注册的观察者对象
         */
        private List<Observer> observers = new ArrayList<Observer>();
      
        /**
         * 注册观察者对象
         *
         * @param observer 观察者对象
         */
        public void attach(Observer observer) {
      
          observers.add(observer);
          System.out.println("Attached an observer");
        }
      
        /**
         * 删除观察者对象
         *
         * @param observer 观察者对象
         */
        public void detach(Observer observer) {
      
          observers.remove(observer);
        }
      
        /**
         * 通知所有注册的观察者对象
         */
        public void nodifyObservers(String newState) {
      
          for (Observer observer : observers) {
            observer.update(newState);
          }
        }
      }
      
      public class ConcreteSubject extends Subject {
      
        private String state;
      
        public String getState() {
          return state;
        }
      
        public void change(String newState) {
          state = newState;
          System.out.println("主题状态为:" + state);
          //状态发生改变,通知各个观察者
          this.nodifyObservers(state);
        }
      }
      
      public interface Observer {
      
        /**
         * 更新接口
         *
         * @param state 更新的状态
         */
        public void update(String state);
      }
      
      public class ConcreteObserver implements Observer {
      
        //观察者的状态
        private String observerState;
      
        @Override
        public void update(String state) {
          /**
           * 更新观察者的状态,使其与目标的状态保持一致
           */
          observerState = state;
          System.out.println("状态为:" + observerState);
        }
      
      }
      
      public class Client {
      
        public static void main(String[] args) {
          //创建主题对象
          ConcreteSubject subject = new ConcreteSubject();
          //创建观察者对象
          Observer observer = new ConcreteObserver();
          //将观察者对象登记到主题对象上
          subject.attach(observer);
          //改变主题对象的状态
          subject.change("new state");
        }
      
      }
      
    5. 责任链模式

    创建多个对象,使这些对象形成一条链,并沿着这条链传递请求,直到链上的某一个对象决定处理此请求。

    纯责任链模式: 如果一个类要么承担责任处理请求要么将请求踢给下一个皮球

    非纯责任链模式:如果一个类承担了一部分责任,还将请求踢给下一个皮球

    1. 应用场景:

    会员等级系统,会员等级之间构成一条链,用户发起一个请求,直到传递到与用户会员匹配的等级

    请假或报销,自己能处理则处理,处理不了则往上报

    1. UML图(图片有误,子类覆写的是report方法handleMessage方法)
    责任链模式
    1. 代码示例

      public abstract class Handler {
      
        private Handler nextHandler;
        private int level;
      
        public Handler(int level) {
          this.level = level;
        }
      
        // 处理请求传递,注意final,子类不可重写
        public final void handleMessage(Request request) {
          if (level == request.getLevel()) {
            this.report(request);
          } else {
            if (this.nextHandler != null) {
              System.out.println("自己无法处理,传递给下一级");
              this.nextHandler.handleMessage(request);
            } else {
              System.out.println("处理链到达尽头,无法处理请求");
            }
          }
        }
      
        public void setNextHandler(Handler handler) {
          this.nextHandler = handler;
        }
      
        // 抽象方法,子类实现
        public abstract void report(Request request);
      
      }
      
      public class Leader extends Handler {
      
        public Leader() {
          super(1);
        }
      
        @Override
        public void report(Request request) {
          System.out.println("Leader 处理:" + request.getDetail());
        }
      }
      
      public class Boss extends Handler {
      
        public Boss() {
          super(2);
        }
      
        @Override
        public void report(Request request) {
          System.out.println("Boss 处理:" + request.getDetail());
        }
      }
      
      public class Client {
      
        public static void main(String[] args) {
          Request requestLevel2 = new Request(2, "Level 2的请求"); // 请求等级高
      
          Boss boss = new Boss();
          Leader leader = new Leader();
          // 设置下一级, 建立职责链
          leader.setNextHandler(boss);
      
          System.out.println("==============开始处理请求=========");
          leader.handleMessage(requestLevel2);
        }
      }
      

    相关文章

      网友评论

          本文标题:设计模式总结

          本文链接:https://www.haomeiwen.com/subject/qutvsqtx.html