目录概要
场景问题
假设要设计一个电脑商场管理系统的某个模块设计,电脑分为品牌和类型两个纬度,我们应该怎么解决?我们初学者最容易想到的办法就是利用继承的方式,那利用继承实现的类图又是什么样子呢?我们看图。
代码展示
package com.aaron.bridge;
假设要设计一个电脑商场管理系统的某个模块设计,电脑分为品牌和类型两个纬度,我们应该怎么解决?我们初学者最容易想到的办法就是利用继承的方式,那利用继承实现的类图又是什么样子呢?我们看图。
代码展示
package com.aaron.bridge;
public interface Computer {
public void sale();
}
class Desktop implements Computer{
@Override
public void sale() {
System.out.println(“台式电脑”);
}
}
class Laptop implements Computer{
@Override
public void sale() {
System.out.println(“笔记本电脑”);
}
}
class Pad implements Computer{
@Override
public void sale() {
System.out.println(“平板电脑”);
}
}
宏碁品牌的三种类型
package com.aaron.bridge;
public class AcerDesktop extends Desktop{
@Override
public void sale() {
System.out.println(“宏碁台式机”);
}
}
class AcerLaptop extends Laptop{
@Override
public void sale() {
System.out.println(“宏碁笔记本电脑”);
}
}
class AcerPad extends Pad{
@Override
public void sale() {
System.out.println(“宏碁平板电脑”);
}
}
苹果品牌的三种类型
package com.aaron.bridge;
public class AppleDesktop extends Desktop{
@Override
public void sale() {
System.out.println(“苹果台式机”);
}
}
class AppleLaptop extends Laptop{
@Override
public void sale() {
System.out.println(“苹果笔记本电脑”);
}
}
class ApplePad extends Pad{
@Override
public void sale() {
System.out.println(“苹果平板电脑”);
}
}
戴尔品牌的三种类型
package com.aaron.bridge;
public class DellDesktop extends Desktop{
@Override
public void sale() {
System.out.println(“戴尔台式机”);
}
}
class DellLaptop extends Laptop{
@Override
public void sale() {
System.out.println(“戴尔笔记本电脑”);
}
}
class DellPad extends Pad{
@Override
public void sale() {
System.out.println(“戴尔平板电脑”);
}
}
思考:假设我们的系统按照上述思路设计。当我们新增一个品牌的时候,怎么办?当然分别要在台式机、笔记本电脑、平板电脑下又添加三个类就搞定。当我们新增一个机器类型的时候又怎么办?当然分别在机器类型又把所有的品牌再添加一次。这样解决起来感觉也不过如此也不难嘛?但是,假设我们电脑商城,添加10个、100个品牌,又添加10种机型,与此同时这个系统时已经开发完毕,只是在维护的系统,那么我们怎么办?这样人工代码会超级痛苦,假设又有很多这样的问题?那么我们岂不是变成无脑码农了?
问题:违背单一职责原则(一个类只由一个维度影响)、开闭原则(对拓展开放,对修改关闭)。
1、什么是桥接模式?
将两个维度(抽象、实现)分离,使它们都可以独立地变化。
2、桥接模式的分析
类型:台式机、笔记本电脑、平板电脑。
品牌:宏碁、苹果、戴尔。
将上述问题分为两个纬度类型和品牌。我们刚刚也讨论了,就是因为拓展性问题,当添加一个新的类型或者品牌的时候,不会对其他类型或者品牌产生影响。只要解决这个问题,那么就大功告成。那我们就从桥接模式的类图开始理解。
Abstraction:
抽象部分的接口。通常在这个对象里面,要维护一个实现部分的对象引用,在抽象对象里面的方法,需要调用实现部分的对象来完成。这个对象里面的方法,通常都是跟具体的业务相关的方法。
RefinedAbstraction:
扩展抽象部分的接口,通常在这些对象里面,定义跟实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法,也可能需要调用实现部分的对象来完成。
Implementor:
定义实现部分的接口,这个接口不用和Abstraction里面的方法一致(根据约定优于配置原则,建议跟Abstraction一致。),通常是由Implementor接口提供基本的操作,而Abstraction里面定义的是基于这些基本操作的业务方法,也就是说Abstraction定义了基于这些基本操作的较高层次的操作。
ConcreteImplementor:
真正实现Implementor接口的对象。
3、桥接模式代码实现
(1)实现部分接口
package com.aaron.bridge;
/**
*
class ConcreteImplementorAcer implements ImplementorBrand{
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> sale() {
System.out.println(</span>"宏碁品牌"<span style="color: #000000;">);
}
}
class ConcreteImplementorApple implements ImplementorBrand{
@Override
public void sale() {
System.out.println(“苹果品牌”);
}
}
class ConcreteImplementorDell implements ImplementorBrand{
@Override
public void sale() {
System.out.println(“戴尔品牌”);
}
}
(2)抽象部分
package com.aaron.bridge;
/**
机器类型纬度-抽象部分
@author xiaoyongAaron
*/
public class AbstractionComputer {
protected ImplementorBrand brand;
public AbstractionComputer(ImplementorBrand brand){
this.brand=brand;
}
public void sale(){
brand.sale();
};
}
/**
扩展部分
@author xiaoyongAaron
*/
class RefinedAbstractionDesktop extends AbstractionComputer{
public RefinedAbstractionDesktop(ImplementorBrand brand) {
super(brand);
}
@Override
public void sale() {
//添加自己的特性,实际业务。
paly();
super.sale();
}
public void paly(){
System.out.println(“我台式机抗摔打”);
}
}
class RefinedAbstractionLaptop extends AbstractionComputer{
public RefinedAbstractionLaptop(ImplementorBrand brand) {
super(brand);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> sale() {
</span><span style="color: #008000;">//</span><span style="color: #008000;">添加自己的特性,实际业务。</span>
lighting();
super.sale();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> lighting(){
System.out.println(</span>"我笔记本电脑充电5分钟,续航5小时"<span style="color: #000000;">);
}
}
class RefinedAbstractionPad extends AbstractionComputer{
public RefinedAbstractionPad(ImplementorBrand brand) {
super(brand);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> sale() {
</span><span style="color: #008000;">//</span><span style="color: #008000;">添加自己的特性,实际业务。</span>
weighting();
super.sale();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> weighting(){
System.out.println(</span>"我平板电脑便于携带"<span style="color: #000000;">);
}
}
(3)客户端调用
package com.aaron.bridge;
public class Clent {
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
</span><span style="color: #008000;">//</span><span style="color: #008000;">品牌纬度-实现部分接口</span>
ImplementorBrand brand=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ConcreteImplementorApple();
</span><span style="color: #008000;">//</span><span style="color: #008000;">机器类型纬度</span>
AbstractionComputer computer=<span style="color: #0000ff;">new</span><span style="color: #000000;"> RefinedAbstractionLaptop(brand);
computer.sale();
}
}
(4)运行结果
1、什么是桥接模式、为什么要桥接?
简单说桥接模式就是把两个纬度分离,所以说当我们在实际开发的时候,遇到两个维度问题的时候,直接条件反射桥接模式。就像上述问题,当有两个维度(品牌+机器类型)赋予给一个类的时候,基于单一职责原则,需要把它们解耦。那通过上述范例可知,那么我们就需要一座桥一样,把两个纬度用一个中间物(类或者接口)把它们关联起来,从而达到我们的目的。
2、桥接模式怎么接?
核心:如何把Implementor对象传递到抽象接口。
(1)如上述描述,利用构造函数传参。
(2)创造无参构造函数,添加get、set方法。
(3)工厂模式:参考设计模式的工厂模式。
(4)IOC控制反转,最经典的就是Spring容器。内部的实现原理原本创建对象都是由我们自己管理,但是把这一步骤交给容器管理,就不用我们担心了。例如在JDBC的设计当中,充当这个角色的就是DriverManage去把对象注入在抽象接口当中。
3、桥接模式本质和经验
(1)本质:抽象与实现(两个纬度)分离。
(2)多用对象组合(has-A),少用继承。
(3)开闭原则:我们应该对代码拓展开放,拒绝代码修改。
参考:https://www.oschina.net/question/1435262_140274
</div>