zoukankan      html  css  js  c++  java
  • 设计模式之桥接模式

    定义

    将抽象部分和它的实现部分分离,使它们都可以独立地变化。在现实生活中,某些类有两个或多个维度的变化,
    如图形既可以按形状分,又可以按颜色分,如何画不同形状不同颜色的图形呢,如果用继承方式,m 种形状 n 种颜色的图形就有 m * n 种,
    不但对应的子类很多,而且扩展困难,桥接模式就能很好的解决这些问题。

    结构

    • Abstraction,抽象部分接口,持有一个实现部分的对象。
    • RefinedAbstraction,扩展抽象部分的对象,调用实现部分的对象来实现具体的业务方法。
    • Implementor,实现部分接口,定义自己的方法供抽象部分调用。
    • ConcreteImplementor,具体的实现部分对象。

    简单实现

    抽象部分接口

    public abstract class Abstraction {
    
      protected Implementor implementor;
    
      public Abstraction(Implementor implementor) {
        this.implementor = implementor;
      }
    
      abstract void operation();
    }
    

    扩展抽象部分

    public class RefinedAbstraction extends Abstraction {
    
      public RefinedAbstraction(Implementor implementor) {
        super(implementor);
      }
    
      @Override
      void operation() {
        implementor.operation();
      }
    }
    

    实现部分接口

    public interface Implementor {
    
      void operation();
    }
    

    具体实现部分对象

    public class ConcreteImplementorA implements Implementor {
    
      @Override
      public void operation() {
        System.out.println("ConcreteImplementorA operation()");
      }
    }
    

    另一个具体实现对象

    public class ConcreteImplementorB implements Implementor {
    
      @Override
      public void operation() {
        System.out.println("ConcreteImplementorB operation()");
      }
    }
    

    客户端

    public class Client {
    
      public static void main(String[] args) {
        Abstraction abstraction = new RefinedAbstraction(new ConcreteImplementorA());
        abstraction.operation();
      }
    
    }
    

    消息系统

    以一个发送各种消息的业务为例,消息按类型可以分为短信消息,邮件消息,按紧急程度可以分为普通消息,加急消息,整个消息系统可以划分为两个维度。

    消息接口

    public interface Msg {
    
      /**
       * 发送消息
       *
       * @param toUser 消息的接受者
       * @param content 要发送消息的内容
       */
      void send(String toUser, String content);
    }
    

    实现部分的接口

    短信消息

    public class SmsMsg implements Msg {
    
      public void send(String toUser, String content) {
        System.out.println("使用短信消息发送'" + content + "'给" + toUser);
      }
    }
    

    邮件消息

    public class EmailMsg implements Msg {
    
      public void send(String toUser, String content) {
        System.out.println("使用邮件消息发送'" + content + "'给" + toUser);
      }
    }
    

    抽象消息类

    /**
     * 抽象消息类
     */
    public abstract class AbstractMsg {
    
      /**
       * 持有实现部分的对象
       */
      protected Msg msg;
    
      public AbstractMsg(Msg msg) {
        this.msg = msg;
      }
    
      public void sendMsg(String toUser, String content) {
        this.msg.send(toUser, content);
      }
    }
    

    抽象部分,内部持有一个实现部分的对象

    普通消息

    /**
     * 普通消息类
     */
    public class NormalMsg extends AbstractMsg {
    
      public NormalMsg(Msg msg) {
        super(msg);
      }
    
      @Override
      public void sendMsg(String toUser, String content) {
        //对于普通消息,直接调用父类方法发送消息即可
        super.sendMsg(toUser, content);
      }
    }
    

    加急消息

    /**
     * 加急消息类
     */
    public class UrgencyMsg extends AbstractMsg {
    
      public UrgencyMsg(Msg msg) {
        super(msg);
      }
    
      @Override
      public void sendMsg(String toUser, String content) {
        content = "【加急】" + content;
        super.sendMsg(toUser, content);
      }
    
      //扩展功能,监控某个消息的处理状态
      public Object watch(String msgId) {
        //根据给出的消息编码(msgId)查询消息的处理状态
        return null;
      }
    }
    

    客户端

    public class Client {
    
      public static void main(String[] args) {
        //发送普通的对象消息
        Msg msg = new SmsMsg();
        AbstractMsg abstractMsg = new NormalMsg(msg);
        abstractMsg.sendMsg("李总", "加班申请速批");
        //发送加急的邮件消息
        msg = new EmailMsg();
        abstractMsg = new UrgencyMsg(msg);
        abstractMsg.sendMsg("李总", "加班申请速批");
      }
    
    }
    

    输出结果为

    使用短信消息发送'加班申请速批'给李总
    使用邮件消息发送'【加急】加班申请速批'给李总
    

    在上述示例中,我们使用桥接模式解耦了"消息类型"和"消息紧急程度"这个两个独立变化的维度。
    后续如果想扩展消息类型如微信消息、钉钉消息等,新建一个类实现Msg接口即可,如果想要扩展紧急程度,
    新建一个类继承AbstractMsg就可以了。

    桥接模式在JDK中的实现

    JDK中的JDBC结构

    应用程序依赖于左边的JDBC的API之上,不依赖具体的数据库驱动,两边都可以独立的进行扩展。

    总结

    优点

    1. 分离抽象和实现部分,提高了系统的灵活性和可扩展性。
    2. 可以在运行时动态地切换实现。

    缺点

    1. 要求正确识别出系统中两个独立变化的维度(抽象和实现),增加了系统的设计难度。

    本质

    桥接模式的本质是分离抽象和实现。分离之后才能独立的变化,才有更好的可扩展性。

    广义上的桥接

    Java中一个很重要的编程原则就是"面向接口编程",也可以叫做"面向抽象编程",使用接口的程序结构图如下

    如果将桥接模式的抽象部分简化一下,暂时不要 RefinedAbstraction 部分,那么就和上图的结构差不多了。

    从某个角度来讲,桥接模式就是"面向抽象编程"设计原则的扩展,通过具体实现的接口将抽象部分和具体的实现部分分离开来,
    抽象部分相当于是使用实现部分接口的客户程序,这样一来,几乎可以把所有面向抽象编程的程序,都视作桥接模式的体现,
    至少也是简化的桥接模式,就算是广义的桥接吧。而Java编程很强调"面向抽象编程",因此,广义的桥接,在Java中可以说是无处不在。

    使用场景

    1. 一个类存在两个或多个独立变化的维度,且多个维度都需要独立的进行扩展。
    2. 希望在抽象和实现之间增加更多的灵活性,动态切换具体的实现。

    参考

    大战设计模式【16】—— 桥接模式
    大战设计模式(第二季)【7】———— 从源码看桥接模式
    设计模式的征途—8.桥接(Bridge)模式
    设计模式(八)——桥接模式
    《JAVA设计模式》之桥接模式(Bridge)
    桥接模式(Bridge模式)详解
    研磨设计模式-书籍

  • 相关阅读:
    NumPy 位运算
    NumPy 数组迭代
    NumPy 广播
    NumPy 基于数值区间创建数组
    NumPy 数组切片
    NumPy 基于已有数据创建数组
    NumPy 数组创建
    NumPy 数据类型
    NumPy ndarray
    区块链技术学习指引
  • 原文地址:https://www.cnblogs.com/strongmore/p/15264645.html
Copyright © 2011-2022 走看看