桥接模式(Bridge Pattern)

概念

桥接(Bridge Pattern)是用于把抽象化与实现化解耦,使得二者可以独立变化。然后两者通过一个抽象类来进行桥接;这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。可以理解为 A功能类、B功能类,然后一个抽象类C作为桥接者,将A和B两者连接起来,这样A、B两者可以修改自己的类的行为,然后C不去桥接两者,这样可以灵活的配置;如果出现很多组合,然而每一个类都实现不同的服务,可能会出现笛卡儿积,这个时候用桥接就特别爽了!

简介

作用及优势

    - 将抽象部分与实现部分分离,使它们都可以独立的变化。

    - 具有较强的扩展能力,单独加实现类就可以多出几个组合。

    - 可以解决子类过多的情况,其实是通过聚合去解决的。

不足之处

其实桥接模式没什么不足之处,就是需要将关联关系或者聚合关系建立在抽象之上,而真正的处理逻辑又在这个抽象的实现中;所以会有点难理解;

场景

    - 需要两两组合又不想通过if else去判断的情况(2 * 2类似的场景)

    - 两边组合都希望能够很好扩展性,且能多出组合关系

    

代码

案例描述

我是个吃货😂,假设有家店,他有很多菜品(Cuisine),这些菜品呢,就是那个桥接者,然后呢,有面食(Pasta)、米粉(RiceFlour);然后厨师会两种做法水煮(Boiled)、爆炒(StirFry);那么笛卡尔积就来,2 * 2 = 4,就存在四个菜品:水煮面、爆炒面、水煮粉、爆炒粉;突然厨子A离职了,然后厨子B来了,假设他是个菜鸟,只会水煮,那么菜品只能减少了(Cuisine);但是只要厨子A又回来了,或者将爆炒改良了成魔鬼辣椒爆炒,但是对于菜品来说,只需要将面食丢给厨子就行了,我菜品上又可以加两道菜;这就是桥接的好处就是应对这种会存在组合关系,会出现笛卡尔积的场景;

工程目录

image.png

项目类图

桥接模式.jpg

具体实现

烹饪方式的接口(CookingMethod)

/**
 * 功能描述: 烹饪方式
 *
 * @author: WuChengXing
 * @create: 2021-06-26 10:25
 **/
public interface CookingMethod {
    /**
     * 制作
     */
    String doThings();
}

具体的烹饪方式:Boiled、StirFry


/**
 * 功能描述: 水煮
 *
 * @author: WuChengXing
 * @create: 2021-06-26 10:27
 **/
public class Boiled implements CookingMethod {

    @Override
    public String doThings() {
        System.out.println("=== 放入水中开始煮 ===");
        return "水煮";
    }
}

---------------------------------------

/**
 * 功能描述: 爆炒
 *
 * @author: WuChengXing
 * @create: 2021-06-26 10:26
 **/
public class StirFry implements CookingMethod {

    @Override
    public String doThings() {
        System.out.println("=== 放入大料中开始爆炒 ===");
        return "爆炒";
    }
} 

菜系的抽象(Cuisine):

/**
 * 功能描述: 菜系
 *
 * @author: WuChengXing
 * @create: 2021-06-26 10:19
 **/
public abstract class Cuisine {

    protected CookingMethod cookingMethod;

    public Cuisine(CookingMethod cookingMethod) {
        this.cookingMethod = cookingMethod;
    }

    /**
     * 制作完成
     */
    public abstract void finishedProduct();
}

这里就是放聚合关系的地方,将烹饪方式 通过聚合方式引入,然后在子类再去处理或者叫使用这个引入的功能,这样烹饪方式和菜品都可以随意的扩展,这个菜系抽象类就相当于一个桥一样,将两者串联起来;

具体的菜系:Pasta、RiceFlour

/**
 * 功能描述: 面食
 *
 * @author: WuChengXing
 * @create: 2021-06-26 10:23
 **/
public class Pasta extends Cuisine {

    public Pasta(CookingMethod cookingMethod) {
        super(cookingMethod);
    }

    @Override
    public void finishedProduct() {
        System.out.println("=== 擀面、醒面、清洗面 ===");
        String name = cookingMethod.doThings();
        System.out.println("=== 制作完成, " + name + "面 ===");
    }
}

-------------------------------------------

/**
 * 功能描述: 米粉
 *
 * @author: WuChengXing
 * @create: 2021-06-26 10:24
 **/
public class RiceFlour extends Cuisine {

    /**
     * 引申出一个问题:为什么子类一定要先调用父类的构造器去协助完成初始化?
     * 道理很简单,就是有一些字段需要父类自己去完成初始化,然后子类才可以去使用
     *
     * @param cookingMethod
     */
    public RiceFlour(CookingMethod cookingMethod) {
        super(cookingMethod);
    }

    @Override
    public void finishedProduct() {
        System.out.println("=== 清洗米粉、浸泡一段时间 ===");
        String name = cookingMethod.doThings();
        System.out.println("=== 制作完成, " + name + "米粉 ===");
    }
} 

测试

/**
 * 功能描述: 桥接模式
 *
 * @author: WuChengXing
 * @create: 2021-06-20 13:07
 **/
public class BridgingTest {
    public static void main(String[] args) {
        Cuisine boiledRiceFlour = new RiceFlour(new Boiled());
        boiledRiceFlour.finishedProduct();
        System.out.println("-----------------------------");
        Cuisine stirFryRiceFlour = new RiceFlour(new StirFry());
        stirFryRiceFlour.finishedProduct();
        System.out.println("-----------------------------");
        Cuisine boiledPasta = new Pasta(new Boiled());
        boiledPasta.finishedProduct();
        System.out.println("-----------------------------");
        Cuisine riceFlourStirFry = new RiceFlour(new StirFry());
        riceFlourStirFry.finishedProduct();

    }
}

结果: 这里就组合出了四种情况,四种菜品;

=== 清洗米粉、浸泡一段时间 ===
=== 放入水中开始煮 ===
=== 制作完成, 水煮米粉 ===
-----------------------------
=== 清洗米粉、浸泡一段时间 ===
=== 放入大料中开始爆炒 ===
=== 制作完成, 爆炒米粉 ===
-----------------------------
=== 擀面、醒面、清洗面 ===
=== 放入水中开始煮 ===
=== 制作完成, 水煮面 ===
-----------------------------
=== 清洗米粉、浸泡一段时间 ===
=== 放入大料中开始爆炒 ===
=== 制作完成, 爆炒米粉 ===