• 桥接模式学习


    背景

    现在要解决源码阶段的继承关系,无法在运行时改变从父类继承的实现。这里用的是手机品牌还有手机中的app,这种问题如何进行解决呢。这就要引入一个模式:桥接模式

    过程

    1、原则:合成/复用原则 :尽量使用合成/聚合原则,不要使用类继承。
    2、定义:将抽象部分和它的实现分离,使他们都可以独立变化。(不是说抽象类和实现类分离,实现指的是抽象类和它的派生类用来实现自己的对象。)

    3、例子:

    如手机品牌和手机app的关系,他们可以是继承关系,也可以是关联关系。

    如果是继承关系的话,是对象的继承关系在编译的时候定义好了,无法在运行时改变从父类继承的实现。子类的视线和它的父类有很紧密的依赖关系,父类实现中的任何变化必然会导致子类发生变化。于是出现了这种聚合关系。

    在这里插入图片描述

    1. Abstraction抽象类
    public abstract class Abstraction {
    
        private Implementor imp;
    
        //约束子类必须实现该构造函数
        public Abstraction(Implementor imp) {
            this.imp = imp;
        }
        
        public Implementor getImp() {
            return imp;
        }
    
        //自身的行为和属性
        public void request() {
            this.imp.doSomething();
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. Implementor抽象类
    public abstract class Implementor {
    
        public abstract void doSomething();
        public abstract void doAnything();
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. ConcreteImplementor
    public class ConcreteImplementorA extends Implementor {
    
        
        public void doSomething() {
            System.out.println("具体实现A的doSomething执行");
        }
        
        
        public void doAnything() {
            System.out.println("具体实现A的doAnything执行");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. RefinedAbstraction
    public class RefinedAbstraction extends Abstraction {
    
        //覆写构造函数
        public RefinedAbstraction(Implementor imp) {
            super(imp);
        }
        
        //修正父类行为
        
        public void request() {
            super.request();
            super.getImp().doAnything();
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. Client客户端
    public class Client {
    
        public static void main(String[] args) {
            Implementor imp = new ConcreteImplementorA();
            Abstraction abs = new RefinedAbstraction(imp);
            abs.request();
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    二、桥接模式的应用
    1. 何时使用

    系统可能有多个角度分类,每一种角度都可能变化时

    2. 方法
    
    • 1

    把这种角度分类分离出来,让它们单独变化,减少它们之间的耦合(合成/聚合复用原则)

    3. 优点
    
    • 1

    抽象和实现分离。桥梁模式完全是为了解决继承的缺点而提出的设计模式
    优秀的扩展能力
    实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装

    4. 缺点
    
    • 1

    会增加系统的理解与设计难度。由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程

    5. 使用场景
    
    • 1

    不希望或不适用使用继承的场景
    接口或抽象类不稳定的场景
    重用性要求较高的场景

    6. 应用实例
    
    • 1

    开关。我们可以看到的开关是抽象的,不用管里面具体怎么实现
    手机品牌与手机软件。两者间有一条聚合线,一个手机品牌可以有多个手机软件

    7. 注意事项
    
    • 1

    不要一涉及继承就考虑该模式,尽可能把变化的因素封装到最细、最小的逻辑单元中,避免风险扩散
    当发现类的继承有n层时,可以考虑使用该模式.

    三、桥接模式的实现
    下面我们举一个例子,就拿上面说的手机品牌与手机软件为例,我们可以让手机既可以按照手机品牌来分类,也可以按手机软件来分类。由于实现的方式有多种,桥接模式的核心意图就是把这些实现独立出来,让它们各自地变化,这就使得没中实现的变化不会影响其他实现,从而达到应对变化的目的。
    在这里插入图片描述

    1. 手机品牌抽象类
    public abstract class HandsetBrand {
    
        protected HandsetSoft soft;
        
        //设置手机软件
        public void setHandsetSoft(HandsetSoft soft) {
            this.soft = soft;
        }
        
        //运行
        public abstract void run();
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 手机软件抽象类
    public abstract class HandsetSoft {
    
        public abstract void run();
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 各类手机品牌
    public class HandsetBrandA extends HandsetBrand {
    
        
        public void run() {
            soft.run();
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 各类手机软件
    public class HandsetGame extends HandsetSoft {
    
        
        public void run() {
            System.out.println("运行手机游戏");
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    5. Client客户端
    
    • 1
    public class Client {
    
        public static void main(String[] args) {
            HandsetBrand ab;
            
            //使用A品牌手机
            ab = new HandsetBrandA();
            System.out.println("A品牌手机:");
            
            ab.setHandsetSoft(new HandsetGame());
            ab.run();
            
            ab.setHandsetSoft(new HandsetAddressList());
            ab.run();
            
            //分隔符
            System.out.println("---------------");
            
            //使用B品牌手机
            ab = new HandsetBrandB();
            System.out.println("B品牌手机:");
            
            ab.setHandsetSoft(new HandsetGame());
            ab.run();
            
            ab.setHandsetSoft(new HandsetAddressList());
            ab.run();
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    总结

    这个桥接模式最深刻的一个点是:将源码定义好的关系修改为运行时在客户端中指定关系。解开源码定义好的类之间的耦合,变成运行的时候才让类之间耦合起来。

    这样我现在如果想要增加一个功能,比如音乐播放器,那么只有增加这个类就可以了,不会影响到其他任何类,类的个数增加也只是一个;如果是要增加S品牌,只需要增加一个品牌的子类就可以了,个数也是一个,不会影响到其他类。这显然符合开放-封闭原则。
    而这里用到的合成/聚合复用原则是一个很有用处的原则,即优先使用对象的合成或聚合,而不是继承。究其原因是因为继承是一种强耦合的结构,父类变,子类就必须变。

  • 相关阅读:
    C++ Opencv之图像数据拷贝分析
    设计模式之建造者模式
    FastDFS-02-JavaAPI
    openssl官网文档资料
    vue部分入门知识点代码示例
    基于python爬虫与数据分析系统设计
    Grom 如何解决 SQL 注入问题
    【Python】从入门到上头—mysql数据库操作模块mysql-connector和PyMySQL应用场景 (15)
    OpenAI 更新 ChatGPT:支持图片和语音输入【附点评】
    C语言ASCII码排序(1086: ASCII码排序(多实例测试))
  • 原文地址:https://blog.csdn.net/weixin_45706856/article/details/134499362