设计模式-Bridge桥接模式

Bridge桥接模式

在这里插入图片描述

解耦抽象和实现,使得两者可以独立的变化。

桥接模式(Bridge Pattern) 也称为 桥梁模式、接口(Interfce)模式 或 柄体(Handle and Body)模式,隶属对象结构型模式。

桥接模式 类似于多重继承方案,但是多重继承方案往往违背了类得单一职责原则,其复用性比较差,桥接模式 是比多重继承更好的替代方案。

桥接模式 的核心在于 解耦抽象和实现

注:此处的 抽象 并不是指 抽象类 或 接口 这种高层概念,实现 也不是 继承 或 接口实现 。抽象 与 实现 其实指的是两种独立变化的维度。其中,抽象 包含 实现,因此,一个 抽象 类的变化可能涉及到多种维度的变化导致的。

角色

  • 抽象(Abstraction):该类持有一个对 实现 角色的引用,抽象 角色中的方法需要 实现 角色来实现。抽象角色一般为抽象类(构造函数规定子类要传入一个 实现 对象);
  • 修正抽象(RefinedAbstraction):Abstraction 的具体实现,对 Abstraction 的方法进行完善和扩展;
  • 实现(Implementor):定义 实现 维度的基本操作,提供给 Abstraction 使用。该类一般为接口或抽象类;
  • 具体实现(ConcreteImplementor):Implementor 的具体实现;

优缺点

优点

  • 抽象和实现分离:这是 桥接模式 的主要特点,也是避免使用 继承 的主要原因。使用 桥接模式,解耦了 抽象 和 实现,使得两者的变化不会对另一方产生影响,使得系统扩展性大大增强。
  • 优秀的扩展能力:桥接模式 的出现就是为了解决多个独立变化的维度的耦合,其高层模块聚合关系已确定(稳定)。因此,无论是 抽象 变化还是 实现 变化,只要对其进行扩展即可,高层代码无需任何更改即可接收扩展。高层代码依赖抽象,严格遵循了 依赖倒置原则。
  • 实现细节对客户透明:由于 桥接模式 在高层模块(抽象层)通过聚合关系构建了稳定的架构(封装)。因此,客户只需与高层模块交互,对 抽象 和实现 的细节完全不需关注。

缺点

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

使用场景

当一个类内部具备两种或多种变化维度时,使用 桥接模式 可以解耦这些变化的维度,使高层代码架构稳定。

实现

例子:我们去咖啡馆喝咖啡,一般有4种选择:大杯加糖,大杯不加糖,小杯加糖,小杯不加糖。那么怎样用代码模拟选择咖啡呢?

分析:按上面的例子,很简单,咖啡就只有4种类型,因此我们建立一个“大杯加糖类”,“大杯不加糖类”,“小杯加糖类“,”小杯不加糖类“就可以覆盖上面例子所列举的咖啡了。但是,这样子的话咖啡杯和味道(糖)就耦合在一起了,后续如果有其他形式的咖啡,我们只能采用继承修改的方式扩展,继承类之间存在冗余部分,因此并不是很好的处理方案。
我们再回顾上面的例子,一杯咖啡包含两种选择:大小杯,有无糖(味道)。也就是通过这两个维度就可以决定一杯咖啡。涉及多个独立维度,我们很清楚应该使用 桥接模式。这里有两个独立维度:大小杯,有无糖(味道)。按 桥接模式 来说,应该有两个 实现:大小杯,有无糖(味道)。但是两个 实现 有点冗余,完全可以把 “大小杯” 这个维度归并到咖啡本身属性中,从而这个例子的维度变成:咖啡大小杯,有无糖(味道)。、


class Client {
    public static void main(String[] args) {
        // 选择咖啡味道:原味
        ICoffeeFlavor flavor = new PlainFlavor();
        // 选择大杯咖啡
        Coffee coffee = new LargeCoffee(flavor);
        // 选择完毕:大杯咖啡:原味
        coffee.makeCoffee();

        // 大杯咖啡:加糖
        new LargeCoffee(new SugarFlavor()).makeCoffee();
        // 小杯咖啡:原味
        new SmallCoffee(new PlainFlavor()).makeCoffee();
        // 小杯咖啡:加糖
        new SmallCoffee(new SugarFlavor()).makeCoffee();
    }

    // Implementor:有无糖
    interface ICoffeeFlavor {
        String addWhat();
    }

    // ConcreteImplementor:原味
    static class PlainFlavor implements ICoffeeFlavor {
        @Override
        public String addWhat() {
            return "原味";
        }
    }

    // ConcreteImplementor:加糖
    static class SugarFlavor implements ICoffeeFlavor {
        @Override
        public String addWhat() {
            return "加糖";
        }
    }

    // Abstraction:咖啡
    static abstract class Coffee {
        protected ICoffeeFlavor mFlavor;

        public Coffee(ICoffeeFlavor flavor) {
            this.mFlavor = flavor;
        }

        public abstract void makeCoffee();
    }

    // RefinedAbstraction:大杯咖啡
    static class LargeCoffee extends Coffee {
        public LargeCoffee(ICoffeeFlavor flavor) {
            super(flavor);
        }

        @Override
        public void makeCoffee() {
            System.out.println("大杯咖啡: " + this.mFlavor.addWhat());
        }
    }

    // RefinedAbstraction:小杯咖啡
    static class SmallCoffee extends Coffee {
        public SmallCoffee(ICoffeeFlavor flavor) {
            super(flavor);
        }

        @Override
        public void makeCoffee() {
            System.out.println("小杯咖啡:" + this.mFlavor.addWhat());
        }
    }
}

上面这个例子,我们采用 桥接模式 解耦了 “咖啡大小杯” 和 “有无糖(味道)” 这两个独立变化的维度。后续如果咖啡馆提供更多的服务,比如中杯咖啡,那么直接新建一个中杯类继承Coffee即可;如果是咖啡味道更改,比如可以加牛奶,蜂蜜等,那么同样只需新建一个类实现ICoffeeFlavor即可。

通过上面的例子,我们能很好地感知到 桥接模式 遵循了设计模式 里氏替换原则依赖倒置原则,最终实现了 开闭原则,对修改关闭,对扩展开放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值