桥接模式

2020-07-23  本文已影响0人  竖起大拇指

1.什么是桥接模式

抽象部分与它的实现部分解耦,使他们都可以独立地变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
桥接模式是一种很实用的结构型设计模式,在软件开发时,如果某个类存在两个独立变化的维度,可以运用桥接模式将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。

与多层继承方案不同,它将两个独立变化的维度设计为两个独立的继承等级结构,并且在抽象层建立一个抽象关联,该关联关系就像一条桥一样,将两个独立继承结构的类联接起来,故名桥接模式。

可以明显看出,桥接模式使用组合代替了继承,将类之间的静态继承关系转换为动态的对象组合关系,使用组合而不用继承,会使系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。

2.示列图

image.png

可以看到在桥接模式的结构图中,存在一条连接两个继承等级结构的桥。

在桥接模式结构图中包含如下几个角色:

桥接模式是一个非常有用的模式,在桥接模式中体现了很多面向对象设计原则的思想,包括“单一职责原则”、“开闭原则”、“合成复用原则”、“里氏代换原则”、“依赖倒转原则”等。

在使用桥接模式时,首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。

3.典型代码

一个类两个独立变化的维度,为了降低耦合度,对两个不同的维度提取抽象类和实现类接口,并建立一个抽象关联关系。对于实现部分维度,实现类接口典型代码如下:

public interface Implementor{
  void operationImpl();
}

具体实现类中实现了在实现类接口中声明的方法,典型代码如下:

public class ConcreteImplementor implements Implementor{
    public void operationImpl(){
          //todo
    }
}

另一维度,抽象类典型代码如下:

public abstract class Abstraction{
  protected Implementor impl; //实现类接口

  public void setImpl(Implementor impl){
        this.impl=impl;
    }

  public abstract void operation();//声明抽象业务方法
}

扩充抽象类继承抽象类,典型代码如下:

public class RefinedAbstaction extends Abstraction{
      public void operation(){
          //todo
          impl.operationImpl();
          //todo
      }
}

看上去有点像 多维度的装饰着模式

4.代码示例

假如有三个品牌的手机vivo,oppo和小米,每个手机需要手机壳保护,手机壳有简单型手机壳和可爱型手机壳。这种处理多维变化(手机和手机壳)的方式运用到软件设计中就是桥接模式。

先定出抽象部分:

public abstract class Phone {
    protected ShellImplementor shellImplementor;

    public void setShellImplementor(ShellImplementor shellImplementor){
        this.shellImplementor = shellImplementor;
    }

    public void call(){
        System.out.println("打电话");
    }

    public abstract void playMusic();
}

扩展抽象部分:

public class VivoPhone extends Phone {
    public void playMusic() {
        shellImplementor.wearShell();
        System.out.println("音乐High起来");
    }
}

public class OppoPhone extends Phone {
    public void playMusic() {
        shellImplementor.wearShell();
        System.out.println("音乐high起来");
    }
}

public class XiaomiPhone extends Phone {
    public void playMusic() {
        shellImplementor.wearShell();
        System.out.println("音乐high起来");
    }
}

将手机壳部分定义为实现部分:

public interface ShellImplementor {
    void wearShell();
}

具体实现:

public class SimpleShell implements ShellImplementor {
    public void wearShell() {
        System.out.println("戴上简单手机壳");
    }
}

public class CuteShell implements ShellImplementor {
    public void wearShell() {
        System.out.println("戴上可爱手机壳");
    }
}

客户端测试:

public class Client {
    public static void main(String[] args) {
        Phone phone = new VivoPhone();
        ShellImplementor shell = new SimpleShell();
        phone.setShellImplementor(shell);
        phone.playMusic();
    }
}

新增手机型号只需继承自Phone就可以,新增手机壳也只需实现ShellImplementor,而且更换简单,可以用XML配置文件实现,只需修改配置,不需修改源码。

而且如果还要多一个实现,即三重维度,比如手机膜,多重继承会直接炸掉,而桥接模式则相对简洁很多。

5.使用场景

在以下情况下可以考虑使用桥接模式:

6.Android中的桥梁模式应用:

在编程实践中,严格按照桥梁模式来开发程序时不常见,前面说过,我们平时经常面对的是具体业务场景,自然而然的使用具体的类来实现功能。让一个具体的类来委托另一个具体的类来实现功能,是最为常见使用场景,很少写出桥梁模式的代码来。不过在一些应用框架中,经常会使用一些标准的桥梁模式,目的是使模块之间能够灵活扩展和解耦,比如Android中就有一些典型的桥梁模式的应用。

下面分别分析一下:

1、AdapterView与Adapter:
对照桥梁模式的定义和结构图,我们发现AdapterView类是抽象角色,Adapter是实现角色,AdapterView所呈现的UI界面不是自己实现的,即它的子View是由Adapter决定的,也就是说AdapterView的职责是实现一组View的展现,但实现的具体方法要委托Adapter来提供。

我们知道,AdapterView有子类ListView、GridView、Spinner。。。等,使用时要给它set一个Adapter类型的对象,这个Adapter也是抽象类,也有一些子类,如ArrayAdapter、SimpleAdapter。。。等。在实际使用时,我们会创建一个具体的XXXAdapter对象,让ListView、GridView等使用,它们之间是独立变化的,没有互相影响,确实是桥梁模式应用。

2、Window与View:

Android中所有的视图View都是通过Window来负责展现的,无论是Activity、Toast、Dialog还是PopupWindow,最终都是要attach到Window上的,Window是View的直接管理者。从Window类的注释可以看出,它是一个顶级窗口外观和行为的抽象基类,提供了标准的UI策略,如背景、标题栏、缺省按键处理等,而响应用户显示操作的由内容View负责处理。

Window对象与View之间是如何协作的呢?就以Activity来说吧,Activity要显示UI界面时,首先要获得一个Window对象,并把各个View控件添加到Window中去。在实际开发时,Activity给它什么View,它就显示什么View。Window把如何显示内容UI(即content view)全部委托给了View类,而View又有一系列的Layout、View、Widget等众多子类,Window类也有子类,它们都可以独立的变化,典型的桥梁模式应用,即Window是抽象角色,而View是实现角色。我们在开发时,可以在Activity中随意创建布局,并把布局作为参数调用setContentView(),只修改Activity就行了,对Window没有半毛影响。

按照桥梁模式的原理,按说Activity也能随意创建一个Window对象,对ContentView里面的View也没半毛关系。不过在当前android的SDK中,Window只有一个子类,即PhoneWindow,这样就成了抽象角色只有一个具体类,Activity也没得选择,不能独立变化,只能使用这个唯一的子类。我们先看一下Window里面的说明:The only existing implementation of this abstract class is android.policy.PhoneWindow, which you should instantiate when needing a Window. Eventually that class will be refactored and a factory method added for creating Window instances without knowing about a particular implementation.大意是Window目前只实现了一个子类PhoneWindow,将来会重构这个类,并使用工厂方法来创建具体的Window对象,至于创建什么样的对象,调用者就不用关注了,工厂会按照一定的场景创建合适的Window对象。这就不得不让我们有这样的思考:比如,如果将来Android手机、AndroidPad和AndroidTV的UI整体界面发生了很大的变化,那么就可以定义一个Window的子类来适应新场景了,比如定义子类PadWindow类用于Pad设备,定义子类TvWindow类用于电视设备,而PhoneWindow只用于手机。工厂方法根据所用设备的类型创建不同的Window对象实例就行了,对市面上已经存在的App应用没有任何影响,同样的View在不同的Window对象上会有不同的展现,也就是说通过该模式已经为将来可能产生的变化做了封装,这也正是Android框架的优秀之处,体现了它的前瞻性。当然这纯属个人猜测,Android框架以后是否会按照上面分析的来演进,拭目以待吧。

上一篇下一篇

猜你喜欢

热点阅读