桥接模式--解耦抽象与实现的设计艺术
其核心思想是将抽象部分与实现部分分离 ,使它们可以独立变化而不互相影响。这种模式通过组合关系替代继承关系 ,有效解决了多维度变化导致的类爆炸问题,提高了系统的灵活性和可扩展性。
让我们来举一个做咖啡为基础的场景说明一些问题: 假设我们的咖啡只有两个维度: 制作咖啡本身的流程makeCoffee,和添加一些不属于咖啡本身的东西(比如、加糖,加奶、加柠檬之类的)。那么现在我们的抽象就是这样的:
csharp
public abstract class BaseCoffee {
public abstract String add();
public abstract void makeCoffee();
}
至于具体的实现,我们可以放在子类当中去做: 因此我们可能会想去制作加糖的美式咖啡:
scala
public class AmericanCoffeeSugar extends BaseCoffee{
@Override
public String add() {
return "加糖";
}
@Override
public void makeCoffee() {
System.out.println("美式咖啡"+add());
}
}
也可能是加奶的意式咖啡:
scala
public class ItalianCoffeeMilk extends BaseCoffee {
@Override
public String add() {
return "加奶";
}
@Override
public void makeCoffee() {
System.out.println("意式咖啡" + add());
}
}
当然,我们现在不考虑好不好喝的问题,为了尽可能的满足客户端的需要,我们必须要提供每种制作工艺(makeCoffee)和额外添加(add)的组合。 现在再回过头来看问题:
-
抽象部分与实现部分分离?
-
当然没有分离,我们的耦合度其实很高。比如如果我想做一个不加糖的美式咖啡,必须要重写一个子类。
-
类爆炸?
-
没错,我们做到了!实际子类的数量就可以达到N(咖啡品种)xM(额外添加物品种)的数量级。
来看桥接模式是怎么做的:
csharp
public abstract class BaseCoffee{
//关键步骤,将会引起某一维度变化的属性抽象为接口,子类中在引入抽象的具体实现
protected CoffeeAdditive additive;
public BaseCoffee(CoffeeAdditive additive){
this.additive = additive;
}
public abstract void makeCoffee();
}
将add这一抽象方法,放在一个接口里,让实现了该接口的类实现该方法。
csharp
public interface CoffeeAdditive {
public String add();
}
具体的添加实现类有:
typescript
public class Lemon implements CoffeeAdditive{
@Override
public String add() {
return "加柠檬";
}
}
typescript
public class Milk implements CoffeeAdditive{
@Override
public String add() {
return "加奶";
}
}
typescript
public class Sugar implements CoffeeAdditive{
@Override
public String add() {
return "加糖";
}
}
那么现在来看,在M维度上,我们只增加了M个实现类对吧。 再来看另一个维度,原来的子类被我们改成如下:
scala
public class AmericanCoffee extends BaseCoffee{
public AmericanCoffee(CoffeeAdditive additive) {
super(additive);
}
@Override
public void makeCoffee() {
System.out.println("美式咖啡" + this.additive.add());
}
}
这时候的子类只需要实现makeCoffee这一方法就好,因为原来的add方法已经被CoffeeAdditive实现类,只需要无脑调用即可。如此,在N维度上我们也只增加了N个类。最终的实现类和子类只会有N+M个。 当在客户端调用时:
java
public class Client {
public static void main(String[] args) {
AmericanCoffee americanCoffee = new AmericanCoffee(new Milk());
americanCoffee.makeCoffee();
Cappuccino cappuccino = new Cappuccino(new Sugar());
cappuccino.makeCoffee();
}
}