桥接模式
桥接模式
标签 : Java与设计模式
场景
在商城系统中商品是分类摆放的,以电脑为例我们有以下商品分类, 该如何良好的处理商品分类销售的问题:
直观上我们会认为该商品分类以继承
来实现:电脑作为根类,台式机/笔记本/平板电脑作为其子类,联想台式机/…作为电脑的孙类.(其继承结构可以从图上直观的看出),但是考虑以下需求:
- 如果我们要增加一个品牌三星?
- 如果我们要增加一个分类智能手机?
问题1的解决方案是在台式机 笔记本 平板电脑 下面都添加相应的子类, 三星台式机 三星笔记本 ….
问题2的解决方案是需在电脑下面添加子类智能手机并在智能手机下在添加三个品牌分类联想智能手机 戴尔只能手机 … 额,想想就够麻烦的,但是如果我们既要添加品牌又要添加分类呢? 要疯了…
这样下去导致的后果就是类的个数急速膨胀, 管理成本极高.
我们马上就意识到仅用继承的是不行的了:
对象的继承关系是在
编译
时就确定好了的,所以无法在运行时改变从父类继承的实现.子类的实现与他的父类有非常紧密的依赖关系, 以至于父类实现中的任何变化必然会导致子类发生变化. 当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换.这种依赖关系限制了灵活性并最终限制了复用性.
这个问题仅用继承是不能解决的,因为这样其实是违反了单一职责原则
:一个类联想笔记本
,却有两个引起这个类变化的原因-类型维度
和品牌维度
.我们把两个维度混合在一起考虑,必然会造成牵一发而动全身的效果.
- 既然这样我们应该从两个维度来思考设计:
类型维度
有自己的一套继承结构,品牌维度
也有自己的一套继承结构,然后中间有座桥(Bridge)
把这两个类关联起来.这样在添加品牌时只需在类型维度做修改就好了, 不会影响到品牌维度;当在品牌维度搞活动时,类型维度也不受影响.桥一端的变化不会引起另一端的变化,这就是桥接模式
:
桥接模式
桥接模式: 将抽象部分与它的实现部分分离, 使他们都可以独立地变化.
其实就是处理多层继承结构, 处理多维变化的场景, 将各个维度设计成独立地继承结构, 使得各个维度可以独立地扩展, 并在抽象层
建立关联.
注意, 这个结构的关键是: Abstraction
里面持有Implementor
对象.
这个反映到我们卖电脑的场景的类图关系如下:
Computer
的brand
属性就是那座桥
.
- 品牌维度
/**
* @author jifang
* @since 16/1/3下午6:25.
*/
public interface Brand {
String brand();
}
class Lenovo implements Brand {
@Override
public String brand() {
return "联想";
}
}
class Dell implements Brand {
@Override
public String brand() {
return "戴尔";
}
}
class Hasee implements Brand {
@Override
public String brand() {
return "神州";
}
}
- 类型维度
/**
* @author jifang
* @since 16/1/3下午6:33.
*/
public abstract class Computer {
private Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public abstract String type();
public void sale() {
System.out.println("我们卖的是<" + brand.brand() + this.type() + ">电脑");
}
}
class Desktop extends Computer {
public Desktop(Brand brand) {
super(brand);
}
@Override
public String type() {
return "台式";
}
}
class Laptop extends Computer {
public Laptop(Brand brand) {
super(brand);
}
@Override
public String type() {
return "笔记本";
}
}
class Pad extends Computer {
public Pad(Brand brand) {
super(brand);
}
@Override
public String type() {
return "平板";
}
}
- Client
public class Client {
@Test
public void test() {
Computer computer = new Desktop(new Dell());
computer.sale();
}
}
现在我要新加一个智能手机类型, 那么只需在Computer
下面添加一个Smartphone
就行了, 品牌维度不需要做任何的改动:
class Smartphone extends Computer {
public Smartphone(Brand brand) {
super(brand);
}
@Override
public String type() {
return "智能手机";
}
}
- Client
public class Client {
@Test
public void test() {
Computer computer = new Smartphone(new Lenovo());
computer.sale();
}
}
小结
- 桥接模式可以取代多层继承的方案. 多层继承违背了
单一职责原则
, 复用性较差, 类的个数过多. 桥接模式可以极大的减少子类的个数, 从而降低管理和维护的成本. - 桥接模式极大的提高了系统的可扩展性, 在两个变化维度中任意扩展一个维度, 都不需要修改原有的系统, 符合
开放-封闭原则
.
桥接模式在实际开发中应用场景:
- JDBC驱动程序
- AWT中的Peer架构
其实只要发现需要从多个角度去分类实现对象, 而只用继承会造成大量类的增加,不能满足开放-封闭原则时,就应该考虑使用桥接模式.