fenlan

Everything gonna be fine in the end, if it's not fine, it's not the end.

0%

设计模式-桥接

概念

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

桥接意图 : 将抽象部分与实现部分分离,使它们都可以独立的变化
主要解决 : 在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活
何时使用 : 实现系统可能有多个角度分类,每一种角度都可能变化
如何解决 : 把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合
主要代码 : 抽象类依赖实现类
桥接优点 : 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明
桥接缺点 : 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程

例子一

我们需要画出一个红色的圆、绿色的圆、红色的正方形、绿色的正方形。在这个例子中,有两个变化的方面,一个是形状、一个是颜色。

普通的设计是这样的 : 有一个圆类和一个正方形类实现Shape类,而圆类下面有两个子类,红色圆和绿色圆;正方形类下面有两个子类,红色正方形、绿色正方形。但是这样的设计会出现的问题是,当我需要添加一个其他变化的颜色时,诸如蓝色,那么情况会非常糟糕。我们需要在每个形状的下面重新添加一个蓝色形状的实现。这就是桥接定义的变化。

为了解决这个问题,我们需要做的事情是将这两个变化放在抽象层,

补充一下,图片有点问题,其中Shape和Color都是抽象的,桥接的抽象独立变化,就是指的Shape和Color可以独立表化而不影响彼此。

例子二

拿汽车在路上行驶的来说。既有小汽车又有公共汽车,它们都不但能在市区中的公路上行驶,也能在高速公路上行驶。这你会发现,对于交通工具(汽车)有不同的类型,它们所行驶的环境(路)也有不同类型,在软件系统中就要适应两个方面(不同车型,不同道路)的变化,怎样实现才能应对这种变化呢?

比较传统的做法

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 //基类 路
class Road {
void run() {
System.out.println("路");
}
}

//市区街道
class Street extends Road {
void run() {
System.out.println("市区街道");
}
}

//高速公路
class SpeedWay extends Road {
void run() {
System.out.println("高速公路");
}
}
//小汽车在市区街道行驶
class CarOnStreet extends Street {
void run() {
System.out.println("小汽车在市区街道行驶");
}
}
//小汽车在高速公路行驶
class CarOnSpeedWay extends SpeedWay {
void run() {
System.out.println("小汽车在高速公路行驶");
}
}
//公交车在市区街道行驶
class BusOnStreet extends Street {
void run() {
System.out.println("公交车在市区街道行驶");
}
}
//公交车在高速公路行驶
class BusOnSpeedWay extends SpeedWay {
void run() {
System.out.println("公交车在高速公路行驶");
}
}
//测试
public static void main(String[] args) {

//小汽车在高速公路行驶
CarOnSpeedWay carOnSpeedWay = new CarOnSpeedWay();
carOnSpeedWay.run();
//公交车在市区街道行驶
BusOnStreet busOnStreet = new BusOnStreet();
busOnStreet.run();
}

但是我们说这样的设计是脆弱的,仔细分析就可以发现,它还是存在很多问题,首先它在遵循开放-封闭原则的同时,违背了类的单一职责原则,即一个类只有一个引起它变化的原因,而这里引起变化的原因却有两个,即路类型的变化和汽车类型的变化;其次是重复代码会很多,不同的汽车在不同的路上行驶也会有一部分的代码是相同的;

再次是类的结构过于复杂,继承关系太多,难于维护,最后最致命的一点是扩展性太差。如果变化沿着汽车的类型和不同的道路两个方向变化,我们会看到这个类的结构会迅速的变庞大。

应用桥接模式来实现

重新设计

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
abstract class AbstractRoad{
AbstractCar aCar;
void run(){};
}
abstract class AbstractCar{
void run(){};
}

class Street extends AbstractRoad{
@Override
void run() {
// TODO Auto-generated method stub
super.run();
aCar.run();
System.out.println("在市区街道行驶");
}
}
class SpeedWay extends AbstractRoad{
@Override
void run() {
// TODO Auto-generated method stub
super.run();
aCar.run();
System.out.println("在高速公路行驶");
}
}
class Car extends AbstractCar{
@Override
void run() {
// TODO Auto-generated method stub
super.run();
System.out.print("小汽车");
}
}
class Bus extends AbstractCar{
@Override
void run() {
// TODO Auto-generated method stub
super.run();
System.out.print("公交车");
}
}

public static void main(String[] args){

AbstractRoad speedWay = new SpeedWay();
speedWay.aCar = new Car();
speedWay.run();

AbstractRoad street = new Street();
street.aCar = new Bus();
street.run();
}

这里将Road 和 Car单独抽象,那么其中Road变化和Car变化,都不会影响相互的代码,也没有出现代码重复。通过对象组合的方式,Bridge 模式把两个角色之间的继承关系改为了耦合的关系,从而使这两者可以从容自若的各自独立的变化,这也是Bridge模式的本意。

这样增加了客户程序与路与汽车的耦合。其实这样的担心是没有必要的,因为这种耦合性是由于对象的创建所带来的,完全可以用创建型模式去解决。在应用时结合创建型设计模式来处理具体的问题。

例子三

你有一个电视机的接口,还有一个遥控器的抽象类。我们都知道,将它们中任何一个定义为一个具体类都不是好办法,因为其它厂家会有不同的实现方法。

代码实现

首先定义电视机的接口:ITV

1
2
3
4
5
public interface ITV {
public void on();
public void off();
public void switchChannel(int channel);
}

实现三星的 ITV 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SamsungTV implements ITV {
@Override
public void on() {
System.out.println("Samsung is turned on.");
}

@Override
public void off() {
System.out.println("Samsung is turned off.");
}

@Override
public void switchChannel(int channel) {
System.out.println("Samsung: channel - " + channel);
}
}

再实现索尼的ITV接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SonyTV implements ITV {

@Override
public void on() {
System.out.println("Sony is turned on.");
}

@Override
public void off() {
System.out.println("Sony is turned off.");
}

@Override
public void switchChannel(int channel) {
System.out.println("Sony: channel - " + channel);
}
}

遥控器要包含对TV的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbstractRemoteControl {

private ITV tv;

public AbstractRemoteControl(ITV tv){
this.tv = tv;
}

public void turnOn(){
tv.on();
}

public void turnOff(){
tv.off();
}

public void setChannel(int channel){
tv.switchChannel(channel);
}
}

定义遥控器的具体类

1
2
3
4
5
6
7
8
9
10
11
public class LogitechRemoteControl extends AbstractRemoteControl {

public LogitechRemoteControl(ITV tv) {
super(tv);
}

public void setChannelKeyboard(int channel){
setChannel(channel);
System.out.println("Logitech use keyword to set channel.");
}
}

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args){
ITV tv = new SonyTV();
LogitechRemoteControl lrc = new LogitechRemoteControl(tv);
lrc.setChannelKeyboard(100);
}
}

参考链接