结构型模式:桥接模式(Bridge Pattern)

来源:互联网 发布:java ee eclipse使用 编辑:程序博客网 时间:2024/06/04 00:29

一、设计模式的分类

(如果以前看过关于设计模式的分类的话,这部分可以忽略!)

经过很多大神的总结,目前Java中一共23种经典的设计模式

按照目的,设计模式可以分为以下三种用途:

1.创建型模式:用来处理对象的创建过程

2.结构型模式:用来处理类或者对象的组合

3.行为型模式:用来对类或对象怎样交互和怎样分配职责进行描述

创建型模式用来处理对象的创建过程,主要包含以下5种设计模式:

 工厂方法模式(Factory Method Pattern)

 抽象工厂模式(Abstract Factory Pattern)

 建造者模式(Builder Pattern)

 原型模式(Prototype Pattern)

 单例模式(Singleton Pattern)

结构型模式用来处理类或者对象的组合,主要包含以下7种设计模式:

 适配器模式(Adapter Pattern)

 桥接模式(Bridge Pattern)

 组合模式(Composite Pattern)

 装饰者模式(Decorator Pattern)

 外观模式(Facade Pattern)

 享元模式(Flyweight Pattern)

 代理模式(Proxy Pattern)

行为型模式用来对类或对象怎样交互和怎样分配职责进行描述,主要包含以下11种设计模式:

 责任链模式(Chain of Responsibility Pattern)

 命令模式(Command Pattern

 解释器模式(Interpreter Pattern)

 迭代器模式(Iterator Pattern)

 中介者模式(Mediator Pattern)

 备忘录模式(Memento Pattern)

 观察者模式(Observer Pattern)

 状态模式(State Pattern)

 策略模式(Strategy Pattern)

 模板方法模式(Template Method Pattern)

 访问者模式(Visitor Pattern) 

本篇文章主要为讲解一下桥接模式(Bridge Pattern)


注:概念性的东西可以忽略不看,可以在看完例子以后再看概念,这样更有利于理解!

二、桥接模式概述

桥接模式主要应对:由于实际的需要,某个类具有两个或两个以上的维度变化,此时如果只是使用继承会使得设计变得相当臃肿,利用桥接模式便能解决这个问题!

桥接模式的做法:把变化部分给抽象出来,使变化部分与主类分离开来,从而将多维度的变化彻底分离,并提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要。

桥接模式的主要特点:把抽象(abstraction)与行为实现(implementation)分离开来,从而可以保持各部分的独立性以及应对它们的功能扩展。

三、桥接模式的应用场景(这部分)

面向对象(OOP)里有类继承的概念,如果一个类或接口有多个具体实现子类,并且这些子类具有以下特性:
--存在相对并列的子类属性。
-- 存在概念上的交叉。
--可变性。
我们就可以用Bridge模式来对其进行抽象与具体,对相关类进行重构。

举例说明:

比如汽车类(Car),假设有2个子类,卡车类(Truck)、公交车类(Bus),它们都有[设置引擎]这个行为,通过设置的不同引擎,可以将它们设置为:1500cc(Car1500)、2000cc(Car2000)等类型的车。
这样,不管是1500cc的卡车还是2000cc的卡车,又或是1500cc的公交车还是2000cc的公交车,它们都可以是汽车类的子类,而且:
- 存在相对并列的子类属性:汽车的种类、汽车引擎规格是汽车的2个并列的属性,没有概念上的重复。
- 存在概念上的交叉:不管是卡车还是公交车,都有1500cc与2000cc引擎规格的车。
- 可变性:除了卡车,公交车之外,可能还有救火车;除了有1500cc与2000cc引擎规格的车之外,还可能有2500cc的车等等。

四、桥接模式解决问题的实例

在【三、桥接模式的应用场景】的举的例子,首先会想到的解决档案是什么?

方案一:

我们比较熟悉的解决方案,通过继承来设计所有可能存在的子类。继承关系如下:

汽车总类:Car
汽车子类 - 按种类分类:Bus,Truck
汽车子类 - 按引擎分类:Bus1500,Bus2000,Truck1500,Truck2000
这样设置引擎这个动作就由各个子类加以实现。

但以后需要增加一种救火车(FireCar),以及增加一个引擎规格2500cc,需要实现的子类将会增加:
Bus2500、Truck2500、FireCar1500、FireCar2000、FireCar2500 多达5个,如果引擎型号多的话,会有更多的子类要追加!
也就是说,这种设计方法,子类数目将随几何级数增长。
而且,Bus1500,Truck1500的引擎规格相同,它们的引擎设置动作应该是一样的,但现在把它们分成不同的子类,难以避免执行重复的动作行为。

方案二:

分别为Bus和Truck实现设置不同引擎的方法
汽车总类:Car
汽车子类:Bus,Truck

然后在Bus类里分别提供1500cc以及2000cc引擎的设置方法:
Bus extends Car {
    public setEngine1500cc();
    public setEngine2000cc();
}

在Truck类里也分别提供1500cc以及2000cc引擎的设置方法:
Truck extends Car {
    public setEngine1500cc();
    public setEngine2000cc();
}

这种情况,子类的数量是被控制了。

但是一方面,如果每增加一种引擎规格,需要修改所有的汽车子类。

    另一方面,即使引擎的设置行为一样,但是不同的汽车子类却需要提供完全一样的方法。

方案三(桥接模式):

在实际的应用开发中,上面的2种方案都会造成迁一发而动全身的效果,而且会存在大量的重复代码,而桥接(Bridge)模式可以很好的解决上面的问题。

1、模型:

Client

    Bridge模式的使用者

Abstraction

    抽象类接口(接口或抽象类)
    维护对行为实现(Implementor)的引用

Refined Abstraction

    Abstraction子类

Implementor

    行为实现类接口 (Abstraction接口定义了基于Implementor接口的更高层次的操作)

ConcreteImplementor

    Implementor子类

2、示例:

让我们来看看怎么应用Bridge模式来设计汽车类。

抽象Abstraction类---->汽车类及其子类:
Car:汽车总类
Truck:汽车子类 - 卡车类。
Bus:汽车子类 - 公交车类。

行为实现Implementor---->汽车引擎设置的行为类及子类。

SetCarEngine:汽车引擎的设置接口

SetCarEngine1500cc:设置1500cc引擎

SetCarEngine2000cc:设置2000cc引擎

使用者Client:

public class Client{
    public static void main( String[] argv ){
        Engine engine1500 = new Engine1500CC();
        Engine engine2200 = new Engine2200CC();
       
        Vehicle bus1500 = new Bus( engine1500 );
        Vehicle bus2200 = new Bus( engine2200 );
        bus1500.setEngine();
        bus2200.setEngine();
       
        Vehicle truck1500 = new Truck( engine1500 );
        Vehicle truck2200 = new Truck( engine2200 );
        truck1500.setEngine();
        truck2200.setEngine();
    }
}

public abstract class Vehicle{
    private Engine engine;

    Vehicle( Engine engine ) {
        this.setEngine( engine );
    }
    public abstract void setEngine();

    public void setEngine( Engine engine ){
        this.engine = engine;
    }

    public Engine getEngine(){
        return engine;
    }
}

Abstraction子类:这里为汽车抽象类的子类:

public class Bus extends Vehicle{
    public Bus( Engine engine) {
        super( engine );
    }
   
    @Override
    public void setEngine() {
        System.out.print("Set Bus Engine: ");
        getEngine().setEngine();
    }
}

Abstraction子类:这里为汽车抽象类的子类:

public class Truck extends Vehicle{
    public Truck( Engine engine ) {
        super( engine );
    }

    @Override
    public void setEngine() {
        System.out.print("Set Truck Engine: ");
        getEngine().setEngine();
    }

}

汽车类的行为接口:

public interface Engine{
    public void setEngine();
}

行为实现子类:

public class Engine2200CC implements Engine{

    public void setEngine() {
        System.out.println("engine 2200CC");
    }  
}

行为实现子类:

public class Engine1500CC implements Engine{

    public void setEngine() {
        System.out.println("engine 1500CC");
    }    
}

 通过上面可以看出,引入桥接模式以后,问题得到了很好的解决!

这个网址上面也是一个很好的实例,建议看一下:http://blog.csdn.net/jason0539/article/details/22568865


四、效果及实现要点:

1.Bridge模式使用"对象间的组合关系",解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。

2.所谓抽象和实现沿着各自维度的变化,即"子类化"它们,在得到各个子类之后,便可以任意使用它们。

3.Bridge模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。

4.Bridge模式的应用一般在"多个非常强的变化维度"。有时候即使有多个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。

五、适用场景:

1.如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。 

2.设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。

3.一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。

4.虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。










0 0
原创粉丝点击