桥接模式,属于结构型设计模式。通过提供抽象与实现之间的桥接结构,把抽象化与实现化解耦,使得二者可以独立变化。
《Head First 设计模式》:
将抽象和实现放在两个不同的类层次中,使它们可以独立地变化。
《图解设计模式》:
将类的功能层次结构和实现层次结构相分离,使二者能够独立地变化,并在两者之间搭建桥梁,实现桥接。
从专业术语对交接模式的解释来看,总是让人似懂非懂,即使懂了,从代码上实现又让人无法捉摸。典型的每个字都认识,连在一起就不懂了。
下面我们先通过一个简单的例子来演示一下桥接的结构是什么样的,然后对其进行改造,最终实现桥接模式。
我们假设一部手机有三个重要部件:电池(Battery)、摄像头(Camera)、屏幕(Screen)。当我们拍摄一张高清照片时,需要充足的电量、高像素的摄像头、高分辨率的屏幕。
于是我们可以通过下面的代码完成拍照动作:
电池Battery
public class Battery {
public Battery() {
System.out.println("充足电量的电池");
}
public void electric() {
System.out.println("电池供电...");
}
}
摄像头Camera
public class Camera {
public Camera() {
System.out.println("高清像素的摄像头");
}
public void catchImg() {
System.out.println("摄像头捕获图像...");
}
}
屏幕Screen
public class Screen {
public Screen() {
System.out.println("高分辨率的屏幕");
}
public void show() {
System.out.println("屏幕显示照片...");
}
}
手机Phone
public class Phone {
private Battery battery;
private Camera camera;
private Screen screen;
public Phone(Battery battery, Camera camera, Screen screen) {
this.battery = battery;
this.camera = camera;
this.screen = screen;
}
public void takePic() {
System.out.println("手机拍照开始...");
// 电池供电
battery.electric();
// 摄像头捕获图像
camera.catchImg();
// 屏幕显示照片
screen.show();
}
}
演示
public static void main(String[] args) {
Battery battery = new Battery();
Camera camera = new Camera();
Screen screen = new Screen();
Phone phone = new Phone(battery, camera, screen);
phone.takePic();
}
结果输出
从该案例中可以看出,电池、摄像头、屏幕这三个组件是相互独立的,各自干各自的活,通过手机将他们连接起来就可以进行拍照,这时手机就表现为桥梁的角色。通过桥梁,三个组件相互独立。
在实际现实中,无论是电池、摄像头、还是屏幕,他们都有各自的品牌厂商,因此我们需要将他们抽象化。如电池有南孚和山羊;摄像头有索尼和徕卡;屏幕有三星和京东方。
所有我们需要做出修改:新建电池、摄像头、屏幕的抽象类;再分别按照品牌厂商对这些抽象类进行实现。
电池抽象类Battery,及其实现类:南孚电池(NanFu)、山羊电池(Sheep)
public interface Battery {
void electric();
}
public class NanFu implements Battery {
public NanFu() {
System.out.println("南孚电池实例化");
}
@Override
public void electric() {
System.out.println("南孚电池正在供电...");
}
}
public class Sheep implements Battery {
public Sheep() {
System.out.println("山羊电池实例化");
}
@Override
public void electric() {
System.out.println("山羊电池正在供电...");
}
}
摄像头抽象类Camera,及其实现类:徕卡摄像头(Laika)、索尼摄像头(Sony)
public interface Camera {
void catchImg();
}
public class Laika implements Camera {
public Laika() {
System.out.println("徕卡摄像头实例化");
}
@Override
public void catchImg() {
System.out.println("徕卡摄像头捕获图像...");
}
}
public class Sony implements Camera {
public Sony() {
System.out.println("索尼摄像头实例化");
}
@Override
public void catchImg() {
System.out.println("索尼摄像头捕获图像...");
}
}
屏幕抽象类Screen,及其实现类:京东方显示屏(JingDongFang)、三星显示屏(SanXing)
public interface Screen {
void show();
}
public class JingDongFang implements Screen {
public JingDongFang() {
System.out.println("京东方显示屏实例化");
}
@Override
public void show() {
System.out.println("京东方显示屏显示照片...");
}
}
public class SanXing implements Screen {
public SanXing() {
System.out.println("三星显示屏实例化");
}
@Override
public void show() {
System.out.println("三星显示屏显示照片...");
}
}
这样一来,手机的构造方法的参数就由原来的具体实现类变成了抽象类。
public Phone(Battery battery, Camera camera, Screen screen) {
this.battery = battery;
this.camera = camera;
this.screen = screen;
}
该构造方法参数的实际类型由调用方创建的实例为准。
public static void main(String[] args) {
// 使用南孚电池
Battery battery = new NanFu();
// 索尼相机
Camera camera = new Sony();
// 京东方显示屏
Screen screen = new JingDongFang();
Phone phone = new Phone(battery, camera, screen);
phone.takePic();
}
输出如下
其实不仅电池、摄像头、屏幕有自己的品牌厂商,手机也不例外,如华为、oppo、vivo等,因此我们也需要将手机这个桥梁的角色抽象化。但是如果我们将该桥梁设计成一个接口,由不同的手机品牌实现该接口,那么就可能会导致不同的实现类具有不同参数的构造方法,如此一来,所有品牌手机的功能虽然受到约束(实现类手机接口),但是他们的组成结构却千差万别。如下所示
public interface MyPhone {
/**
* 拍照
*/
void takePic();
/**
* 通话
*/
void call();
/**
* 微信聊天
*/
void wechat();
}
public class Oppo implements MyPhone{
private ComponentA componentA;
private ComponentB componentB;
public Oppo(ComponentA componentA, ComponentB componentB) {
this.componentA = componentA;
this.componentB = componentB;
}
@Override
public void takePic() {
// 照相
}
@Override
public void call() {
// 打电话
}
@Override
public void wechat() {
// 聊微信
}
}
public class Vivo implements MyPhone{
private ComponentC componentC;
private ComponentD componentD;
public Oppo(ComponentC componentC, ComponentD componentD) {
this.componentC = componentC;
this.componentD = componentD;
}
@Override
public void takePic() {
// 照相
}
@Override
public void call() {
// 打电话
}
@Override
public void wechat() {
// 聊微信
}
}
从上面的代码来看,oppo和vivo虽然实现了**手机(MyPhone)**定义的所有功能,但是却乱七八糟的,oppo手机内部组件是ComponentA
和ComponentB
,vivo手机内部组件却是ComponentC
和ComponentD
。这样的话手机行业岂不乱套了。
所以我们对桥梁的抽象化不应采用接口,而是抽象类。
使用抽象类有一个好处是,可以使所有子类拥有相同的内部属性,而且对所有子类的构造方法也做出了约束。
如下所示,我们将手机抽象化一个手机接口(Phone)来定义各个功能,再通过一个抽象子类(AbstractPhone)实现手机接口定义的功能,并规范构造方法,由华为(HuaWei)、**小米(XiaoMi)**两个品牌继承该抽象子类。
public interface Phone {
/**
* 拍照
*/
void takePic();
}
public abstract class AbstractPhone implements Phone {
private Battery battery;
private Camera camera;
private Screen screen;
public AbstractPhone(Battery battery, Camera camera, Screen screen) {
this.battery = battery;
this.camera = camera;
this.screen = screen;
}
@Override
public void takePic() {
System.out.println("手机拍照开始...");
// 电池供电
battery.electric();
// 摄像头捕获图像
camera.catchImg();
// 屏幕显示照片
screen.show();
}
}
public class HuaWei extends AbstractPhone {
public HuaWei(Battery battery, Camera camera, Screen screen) {
super(battery, camera, screen);
System.out.println("华为手机实例化");
}
}
public class XiaoMi extends AbstractPhone {
public XiaoMi(Battery battery, Camera camera, Screen screen) {
super(battery, camera, screen);
System.out.println("小米手机实例化");
}
}
通过接口(定义功能)、抽象子类(桥梁)、**实现类(实现功能)**的方式,就是交接设计模式的实现。
下面我们进行代码测试
public static void main(String[] args) {
Battery battery = new NanFu();
Camera camera = new Sony();
Screen screen = new JingDongFang();
// 华为将南孚电池、索尼相机、京东方显示屏桥接起来形成一部手机
Phone phone = new HuaWei(battery, camera, screen);
// 使用华为手机拍照
phone.takePic();
}
以上就是桥接模式的演变过程,希望通过本篇文章的阅读,能使各位朋友对桥接模式有更深入的理解。
纸上得来终觉浅,绝知此事要躬行。
————————我是万万岁,我们下期再见————————