设计模式——结构型之用桥梁模式(Bridge Pattern)将“抽象”与“实现”解耦(五)

引言

相信对于现实生活中这样的情况都不陌生,比如说开关与它具体控制的电器,开关的类型多种多样,而电器也是千变万化,两者之间相对独立变化却又耦合在一起,再比如说奶茶店的奶茶,有不同规格大小、不同口味、不同味道浓度、不同温度的类别等等,假如使用程序语言来描述的话,使用普通的继承结构一定相当臃肿而且高度耦合,基于此桥梁模式应运而生。

一、桥梁模式概述

桥梁模式(Bridge Pattern)是一种简单的结构型设计模式,又称桥接模式,可以将抽象和实现解耦,使得两者可以独立地变化。(Decouple an abstraction from its implementation so that the two can vary independently)所谓耦合就是指两个实体的行为的某种强关联,而将它们的强关联去掉即为解耦(是指将抽象化和实例化之间的耦合解脱开,或者是将它们之间的强关联改成弱关联,而在编译时期已经确定的就是强关联,无法在运行时期动态改变的关联;可以动态确定并且可以在运行时期动态改变的关联就是弱关联,其中继承关系是强关联,而聚合关系是弱关联)将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联,因此,桥梁模式中的解耦思想:在一个软件系统中的抽象化和实现化之间使用聚合关系,而不是继承关系,从而使两者可以相对对立的变化
这里写图片描述
通常使用桥梁模式的系统含有两个等级结构:由抽象化角色和修正抽象化角色组成的抽象化等级结构由实现化角色和两个具体实现化角色所组成的实现化等级结构,两个等级结构中又可以分四种角色:

  • 抽象化角色(Abstraction)——抽象化给出的定义,并保存一个对实现化对象的引用,它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。

  • 修正抽象化角色(RefineAbstraction)——拓展抽象化角色,改变和修正父类对抽象化的定义。

  • 实现化角色(Implementor)——这个角色给出实现化角色的接口,但是不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作,定义角色必需的行为和属性。

  • 具体实现化角色(ConcreteImplementor)——这个角色给出实现化角色接口的具体实现。

总而言之,抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的

二、桥梁模式的优缺点和可用场景

1、桥梁模式的优点

  • 分离抽象和实现部分,桥梁模式分离了抽象部分和实现部分,从而极大地提供了系统的灵活性。让抽象部分和实现部分独立出来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。

  • 更好的扩展性,桥梁模式使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,从而大大提高了系统的可扩展性

  • 实现细节对客户透明,客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。

2、桥梁模式的缺点

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进

  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

3、桥梁模式的可用场景和注意事项

  • 不希望或不适用使用继承的场景,例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式,但使用该模式时主要考虑如何拆分抽象和实现,并不是一涉及继承就要考虑使用该模式,

  • 接口或抽象类不稳定的场景,明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失
    败的做法。

  • 重用性要求较高的场景,设计的颗粒度越细,则被重用的可能性就越大,而采用继承则受父类的限制,不可能出现太细的颗粒度。

  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时

三、桥梁模式的实现

其实在桥梁模式中的“抽象”和“实现”并不是传统意义上的对应的关系,而是对应着两个独立的维度,为了区分而抽象出来的概念而已,事实上任何维度变化类或多个树状类族之间的耦合都可以通过桥梁模式来实现解耦,接下以以奶茶店卖饮料为例,从配料上区分有奶茶、绿茶、红茶、水果茶等等,从规格维度上区分还有大杯、小杯,先抽象出Tea对象于是两个维度组合起来可以有大杯红茶、小杯红茶等等多种产品。

1、定义抽象化角色

本质就是先抽象出一个维度

package bridge;

/**
 * 以奶茶店卖饮料为例,从配料上区分有奶茶、绿茶、红茶、水果茶等等,从规格维度上区分还有大杯、小杯,先抽象出Tea对象
 * 于是两个维度组合起来可以有大杯红茶、小杯红茶等等多种产品
 */

public abstract class AbstractTea {
    protected AbstractTeaIngredient teaIngredient;//持有配料对象的引用

    public AbstractTea(AbstractTeaIngredient ingredient){
        this.teaIngredient=ingredient;
    }
    //奶茶最终成品都是延迟到子类去实现
    public abstract void makeTea();
}

2、修正抽象化角色

package bridge;

//由于不同饮料需要添加的配料不同,也把配料抽象出来
public abstract class AbstractTeaIngredient {
    public abstract String putIn();
}

3、实现化角色

package bridge;

//从规格维度的实现
public class LargeCubTea extends AbstractTea {

    public LargeCubTea(AbstractTeaIngredient ingredient) {
        super(ingredient);
    }

    @Override
    public void makeTea() {
        System.out.println("制作大杯的"+teaIngredient.putIn());  
    }

}


public class SmallCubTea extends AbstractTea {

    public SmallCubTea(AbstractTeaIngredient ingredient) {
        super(ingredient);
    }

    @Override
    public void makeTea() {
        System.out.println("制作小杯的"+teaIngredient.putIn());  
    }

}

4、具体实现化角色

package bridge;

public class RedTeaIngredient extends AbstractTeaIngredient {

    @Override
    public String putIn() {
        return "红茶";
    }
}

//从配料维度去实现
public class GreenTeaIngredient extends AbstractTeaIngredient {

    @Override
    public String putIn() {
        return "绿茶";
    }
}

测试

package bridge;

public class Client {

    public static void main(String[] args) {
        AbstractTea tea=null;
        AbstractTeaIngredient ingredient=null;
        //制作大杯红茶
        ingredient=new RedTeaIngredient();
        tea=new LargeCubTea(ingredient);
        tea.makeTea();
        //制作大杯绿茶
        ingredient=new GreenTeaIngredient();
        tea=new LargeCubTea(ingredient);
        tea.makeTea();

        //制作小杯红茶
        ingredient=new RedTeaIngredient();
        tea=new SmallCubTea(ingredient);
        tea.makeTea();
        //制作小杯绿茶
        ingredient=new GreenTeaIngredient();
        tea=new SmallCubTea(ingredient);
        tea.makeTea();
    }
}

假如后期需要增加饮料的规格或者品种时,扩展十分简单只要增加对应的实现类即可。

public class MidCubTea extends AbstractTea {

    public MidCubTea(AbstractTeaIngredient ingredient) {
        super(ingredient);
    }

    @Override
    public void makeTea() {
        System.out.println("制作中杯的"+teaIngredient.putIn());  
    }

}

猜你喜欢

转载自blog.csdn.net/CrazyMo_/article/details/79755405
今日推荐