【设计模式(七)】结构型模式之桥接模式

个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充


前言

设想如果要绘制矩形、圆形、椭圆、正方形4个形状,且各有红、黄、蓝3种颜色,那么显然一共有12种不同的结果

如果有m个形状,n个颜色,那么需要定义m*n个类,看上去可以接受是吗?

那如果要新增一种颜色,就得多定义n个类,同理增加一种形状也需要增加m个类,修改的话同理也要修改大量的类

这个维护成本可就高的过分了,耦合度太高,而且违背了单一职责原则,那咋办嘛?

换个思路,其实可以将形状与颜色分离开,再将其将其进行组合,即可得到需要的结果

这样一来,形状和颜色从继承关系变成了关联关系,因此将可以独立变化,互不影响,那么新增形状或颜色都只需要增加一个类,变更也只需要修改一个类

万事分个主次,我们不妨将形状为主,颜色为次,颜色作为形状的一种属性

同理,形状也可以拥有其它属性,比如渐变度、透明度、边框等等,都可以用桥接模式组合在一起,且独立变化


桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。


1.介绍

使用目的:将抽象部分与它的实现部分分离开来,使他们都可以独立变化

使用时机:实现系统可能有多个角度分类,每一种角度都可能变化,

解决问题:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

实现方法:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合

应用实例

  1. 奶茶会有主材、温度、配料等多个独立的选项,互不影响
  2. Timer().schedule(new TimerTask(){}),需要传入一个TimerTask对象,我们可以对其进行自定义,只要保持对象的类继承TimerTask就行了

优点

  1. 抽象和实现的分离。
  2. 优秀的扩展能力。
  3. 实现细节对客户透明。

缺点

  1. 提高了系统的抽象程度,也就一定程度上增加系统复杂度
  2. 由于聚合关联关系建立在抽象层,开发者需要针对抽象进行设计与编程
  3. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。


简而言之,就是避免继承关系,转而建立关联关系,进而脱耦,再进行组合


概念补充

鉴于出现了一些较为抽象的概念,这里进行一下补充解释

  • 抽象化:将复杂物体的一个或几个特性抽出去而只注意其他特性的行动或过程。在面向对象就是将对象共同的性质抽取出去而形成类的过程

    比如在前言中,我们将颜色抽离出去,而只在意形状,也就是将颜色抽象化了

  • 实现化:针对抽象化给出的具体实现。它和抽象化是一个互逆的过程,实现化是对抽象化事物的进一步具体化。

    虽然将颜色抽离出去了,但是每种颜色依然需要实现,生成自己类,也就是将颜色实现化

  • 脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系

    我们将颜色和形状分离开了,使得两者可以独立变化,只需要保证能对接上即可,也就是将颜色与形状脱耦

桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。


2.结构

桥接模式通常包括4个主要角色

  • 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
image-20201021144635643
  • 客户端调用抽象类Shape(抽象化角色)
    • 抽象类Shape持有接口类ColorBorder,作为其两项属性(实现化角色)
      • 实体类BlueRed实现Color,作为具体的颜色属性(具体实现化角色)
      • 实体类NormalBorder实现Border,作为具体的边框属性(具体实现化角色)
    • 实体类CircleSquareRectangle继承Shape,并实现抽象方法,作为具体的实体(扩展抽象化角色)

Shape、Color、Border自身均无法实例化,我们需要实例化各个具体属性实体,并组装在一起,进而构成一个整体


3.实现

模拟业务如下:

  • 需要目标角色包括两个属性,颜色Color和形状Shape,并拥有方法draw()输出当前角色信息
  • 颜色已有两种RedBlue
  • 形状已有两种CircleSquare

我们将Shape作为抽象化角色,Color作为实现化角色

==实际应用中根据需求角色哪种属性作为抽象化角色,其余属性作为实现化角色==

  1. 定义接口Color(实现化角色)

    interface Color {
        String drawColor();
    }
    
  2. 定义类RedBlue,实现Color(具体实现化角色)

    class Red implements Color {
        @Override
        public String drawColor() {
            return "red";
        }
    }
    
    class Blue implements Color {
        @Override
        public String drawColor() {
            return "blue";
        }
    }
    
  3. 定义抽象类Shape(抽象化角色)

    abstract class Shape {
        protected Color color;
    
        public Shape(Color color) {
            this.color = color;
        }
    
        abstract void draw();
    }
    

    请注意,这里使用构造函数引用color,也可以使用别的方法,如提供setShape(Color color)方法等均可

  4. 定义类CircleSquare(扩展抽象化角色)

    class Circle extends Shape {
    
        public Circle(Color color) {
            super(color);
        }
    
        @Override
        void draw() {
            System.out.println("draw a circle with " + color.drawColor());
        }
    }
    
    class Square extends Shape {
    
        public Square(Color color) {
            super(color);
        }
    
        @Override
        void draw() {
            System.out.println("draw a square with " + color.drawColor());
        }
    }
    
  5. 客户端调用

    public class BridgeTest {
        public static void main(String[] args) {
            Shape redCircle = new Circle(new Red());
            redCircle.draw();
    
            Shape blueSquare = new Square(new Blue());
            blueSquare.draw();
        }
    
    }
    

完整代码

package com.company.test.bridge;

interface Color {
    String drawColor();
}

class Red implements Color {
    @Override
    public String drawColor() {
        return "red";
    }
}

class Blue implements Color {
    @Override
    public String drawColor() {
        return "blue";
    }
}

abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

class Circle extends Shape {

    public Circle(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.println("draw a circle with " + color.drawColor());
    }
}

class Square extends Shape {

    public Square(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.println("draw a square with " + color.drawColor());
    }
}

public class BridgeTest {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new Red());
        redCircle.draw();

        Shape blueSquare = new Square(new Blue());
        blueSquare.draw();
    }

}

运行结果

image-20201021151849099

4.扩展使用

桥接模式与适配器模式的联用

桥接模式可以将多种属性组装在一起,分离为抽象化角色和实现化角色,那如果无法兼容呢?

比如调用了大量第三方接口,甚至是开发者之间未提前约定好接口,都会出现不兼容的情况

这个时候就可以使用适配器模式,对接口进行转换,使其能够兼容,进而在一起协同工作

在开发过程中,也可以使用缺省适配器来规范接口,暂未实现的可以缺省


后记

桥接模式的核心思想就是使用关联关系替换继承关系,从而将一个整体分成不同的组件,而不是不同层级

好处是脱耦,便于扩展和维护

这种思想才是我们学习的重点,能帮助我们写出更优质的代码,而不仅仅是个代码的搬运工而已


作者:Echo_Ye

WX:Echo_YeZ

Email :[email protected]

个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

你可能感兴趣的:(【设计模式(七)】结构型模式之桥接模式)