模式说明
当一个事物可以通过多个维度描述时,要避免多个维度层层继承该事物的抽象类,而应该将这些不同的维度从事物中分离出来独立变化。例如电脑可以通过不同品牌和不同软件描述,可以将电脑作为一个PC
抽象类,以品牌为主体,不同品牌继承该PC
类如ApplePC
。软件分离出来成为一个独立的抽象类Software
,具体软件继承该Software
抽象类,例如Browser
类。在PC抽象类中以组合形式拥有Software
,并通过调用Software
的方法,实施Software
中的行为。在UML图中分离事物抽象类通过带组合箭头的线与主体事物抽象类连接,故名桥接。
本示例演示上述PC场景。如果希望增加一个华为品牌PC只需增加继承PC
类的HuaweiPC
类即可,无需改动其他类。同理希望增加视频软件时,也只需要增加VideoPlayer
类而无需关注品牌。这就是各自独立变化。另,本示例中主体事物PC
只持有一个Software
实例,如果想同时持有多个Software
实例,可以声明为集合类型(如List
),然后增加相应的add
方法。
结构
抽象主体事物类
主体事物,类内声明一个以protected修饰的分离事物(以抽象类型声明)。声明一个用于实施分离事物行为的抽象方法。
具体主体事物类
继承抽象主体事物类,实现抽象方法。
抽象分离事物类
能够描述主体事物的某个维度的事物,声明自己的行为。
具体分离事物类
继承抽象分离事物类,实现抽象方法。
代码示例
package com.yukiyama.pattern.structure;
/**
* 桥接模式
*/
public class BridgeDemo {
public static void main(String[] args) {
// 声明一个PC(主体事物)
PC pc = new ApplePC();
// 声明一个软件(分离事物)
Software soft1 = new Browser();
// 将软件组合进PC中(主体持有分离)
pc.setSoftware(soft1);
// 通过调用主体事物的方法来间接执行分离事物的方法
// 输出"启动: Browser"
pc.run();
// 增加一个软件后,同上
Software soft2 = new MusicPlayer();
pc.setSoftware(soft2);
// 输出"启动: MusicPlayer"
pc.run();
}
}
/**
* 主体事物抽象类
* 以PC为例,内部声明protected修饰的分离事物Software。非抽象方法
* setSoftware()传入分离事物使得主体持有分离。声明用于实施Software
* 行为抽象方法run()。
*/
abstract class PC{
protected Software soft;
public void setSoftware(Software soft) {
this.soft = soft;
}
public abstract void run();
}
/**
* 主体事物具体类
* 继承抽象主体事物类,实现抽象方法run,内部实际调用Software自身的run。
*/
class ApplePC extends PC{
@Override
public void run() {
this.soft.run();
}
}
/**
* 抽象分离事物类
* 有自身的属性和方法。
* 下例是从软件维度描述PC的软件抽象类。
*/
abstract class Software{
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract void run();
}
/**
* 具体分离事物类
* 继承抽象分离事物类,实现抽抽象方法。
* 下例是浏览器类。
*/
class Browser extends Software{
public Browser() {
this.setName("Browser");
}
@Override
public void run() {
System.out.println("启动: "+this.getName());
}
}
/**
* 具体分离事物类
* 下例是音乐播放器类。
*/
class MusicPlayer extends Software{
public MusicPlayer() {
this.setName("MusicPlayer");
}
@Override
public void run() {
System.out.println("启动: "+this.getName());
}
}