文章42
标签13
分类3

桥接模式

Bridge

桥接模式

是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

桥接模式即将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。

实例

电视机厂家为了减少成本,将遥控器生产外包,我们公司就接到了Sony和TCL的遥控器项目。
现状:Sony和TCL厂家的接口设计好了,并分别提供了相关的类。
我们遥控器所要做的就是继承Sony和TCL的类,调用各自的方法,就可以控制Sony和TCL的电视;需要把遥控器的界面、功能设计好。

电视厂家共同继承的接口

package com.greedring;

public interface Control {
    public void on();

    public void off();

    public void setChannel(int channel);
}

Sony厂商提供的遥控器类

package com.greedring;

public class SonyControl implements Control {
    @Override
    public void on() {
        System.out.println("Turn on the Sony TV...");
    }

    @Override
    public void off() {
        System.out.println("Turn off the Sony TV...");
    }

    @Override
    public void setChannel(int channel) {
        System.out.println("The Sony TV Channel is setted " + channel + "...");
    }
}

TCL厂商提供的遥控器类

package com.greedring;

public class TCLControl implements Control {
    @Override
    public void on() {
        System.out.println("Turn on the TCL TV...");
    }

    @Override
    public void off() {
        System.out.println("Turn off the TCL TV...");
    }

    @Override
    public void setChannel(int channel) {
        System.out.println("The TCL TV Channel is setted " + channel + "...");
    }
}

我们作为遥控器厂家设置的集结方案

package com.greedring;

public interface TVControl {
    public void onOff();

    public void nextChannel();

    public void preChannel();
}

我们完成的Sony遥控器

package com.greedring;

public class SonyTVControl extends SonyControl implements TVControl {
    private static int channel = 0;
    private static boolean isOn = false;

    @Override
    public void onOff() {
        if (isOn) {
            isOn = false;
            super.off();
        } else {
            isOn = true;
            super.on();
        }
    }

    @Override
    public void nextChannel() {
        channel++;
        if (channel > 233) {
            channel = 0;
        }
        super.setChannel(channel);
    }

    @Override
    public void preChannel() {
        channel--;
        if (channel < 0) {
            channel = 233;
        }
        super.setChannel(channel);
    }
}

我们完成的TCL遥控器

package com.greedring;

public class TCLTVControl extends TCLControl implements TVControl {
    private static int channel = 0;
    private static boolean isOn = false;

    @Override
    public void onOff() {
        if (isOn) {
            isOn = false;
            super.off();
        } else {
            isOn = true;
            super.on();
        }
    }

    @Override
    public void nextChannel() {
        channel++;
        if (channel > 233) {
            channel = 0;
        }
        super.setChannel(channel);
    }

    @Override
    public void preChannel() {
        channel--;
        if (channel < 0) {
            channel = 233;
        }
        super.setChannel(channel);
    }
}

测试我们完成的两个遥控器

package com.greedring;

public class MainAPP {
    public static void main(String[] args) {
        SonyTVControl sonyTVControl = new SonyTVControl();
        TCLTVControl tcltvControl = new TCLTVControl();

        sonyTVControl.onOff();
        sonyTVControl.nextChannel();
        sonyTVControl.nextChannel();
        sonyTVControl.preChannel();
        sonyTVControl.onOff();

        System.out.println("==============================");

        tcltvControl.onOff();
        tcltvControl.preChannel();
        tcltvControl.nextChannel();
        tcltvControl.onOff();
    }
}

需求拓展

随着社会的发展,我们有了新的需求:
1.别的厂家发来新的外包
2.客户需要新功能,比如说我想添加一个回看功能

每次出现新需求就重写集结方案过于复杂,我们就需要引入桥接模式

  • 原来使用的集结接口,现在我们做成抽象类。在类里面放一个遥控器的接口 ,这个接口指向了所有遥控器厂家都实现的接口。
  • 因为Sony、TCL和Sharp都是基于这个接口来做的,我们在这个接口里放置一个变量 ,里面所有的函数功能都是调用这个变量来实现的。
  • 那么我们基于这个接口来做的集结遥控器和新类型遥控器,所有的功能都可以调用 底层提供的接口对象。
  • 这个接口对象设置的时候,比如我们设置成Sony、TCL、Sharp的类,集结遥控器和新类型遥控器调用的时候就是调用接口里的对象。
  • 也就是说集结遥控器和新类型遥控器在调用的时候,不清楚调用的是哪个遥控器厂家提供的类对象。用哪个厂家的,我们就调用哪个厂家的电视机。

这时候我们再来看

桥接模式的原理:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
    将遥控器的实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。

创建抽象类TVControlabs

package com.greedring;

public abstract class TVControlabs {
    public Control mControl;

    public Control getmControl() {
        return mControl;
    }

    public void setmControl(Control mControl) {
        this.mControl = mControl;
    }

    public TVControlabs(Control mControl) {
        this.mControl = mControl;
    }

    public TVControlabs() {
    }

    public abstract void onOff();

    public abstract void nextChannel();

    public abstract void preChannel();
}

我们先测试通过TVControl1继承TVControlabs类,来完成未更新的需求

package com.greedring;

public class TVControl1 extends TVControlabs {
    private static int channel = 0;
    private static boolean isOn = false;

    public TVControl1(Control mControl) {
        super(mControl);
    }

    public TVControl1() {
    }

    @Override
    public void onOff() {
        if (isOn) {
            isOn = false;
            mControl.off();
        } else {
            isOn = true;
            mControl.on();
        }
    }

    @Override
    public void nextChannel() {
        channel++;
        if (channel > 233) {
            channel = 0;
        }
        mControl.setChannel(channel);
    }

    @Override
    public void preChannel() {
        channel--;
        if (channel < 0) {
            channel = 233;
        }
        mControl.setChannel(channel);
    }
}

通过主函数测试

package com.greedring;

public class MainApp1 {
    public static void main(String[] args) {
        TVControl1 mSonyControl = new TVControl1(new SonyControl());
        mSonyControl.onOff();
        mSonyControl.preChannel();
        mSonyControl.nextChannel();
        mSonyControl.onOff();
    }
}

为拓展需求,创建新的类NewTVControl继承自TVControlabs

package com.greedring;

public class NewTVControl extends TVControlabs {
    private static int channel = 0;
    private static boolean isOn = false;
    private static int backChannel = 0;

    public NewTVControl(Control mControl) {
        super(mControl);
    }

    public NewTVControl() {
    }

    @Override
    public void onOff() {
        if (isOn) {
            isOn = false;
            mControl.off();
        } else {
            isOn = true;
            mControl.on();
        }
    }

    @Override
    public void nextChannel() {
        backChannel = channel;
        channel++;
        if (channel > 233) {
            channel = 0;
        }
        mControl.setChannel(channel);
    }

    @Override
    public void preChannel() {
        backChannel = channel;
        channel--;
        if (channel < 0) {
            channel = 233;
        }
        mControl.setChannel(channel);
    }

    public void back() {
        int change = channel;
        channel = backChannel;
        backChannel = change;
        mControl.setChannel(channel);
    }
}

通过主函数测试新需求

package com.greedring;

public class MainApp2 {
    public static void main(String[] args) {
        NewTVControl mSharpControl = new NewTVControl(new SharpControl());
        mSharpControl.onOff();
        mSharpControl.nextChannel();
        mSharpControl.back();
        mSharpControl.preChannel();
        mSharpControl.onOff();
    }
}

优点:
1、分离抽象接口及其实现部分。提高了比继承更好的解决方案。
2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
3、实现细节对客户透明,可以对用户隐藏实现细节。

缺点:
1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

总结:
1、桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。
2、对于两个独立变化的维度,使用桥接模式再适合不过了。
3、对于“具体的抽象类”所做的改变,是不会影响到客户。

本文作者:GreedRing
本文链接:http://greedring.com/2019/08/29/Bridge/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可