设计模式学习笔记之 Bridge
Bridge模式是一种将抽象部分与它的实现部分分离,使它们都可以独立地变化的设计模式。通过Bridge模式,我们可以应对类型在多个维度上发生的变化。
我们一个食堂的例子。在食堂有两种套餐,Meal_A和Meal_B,同时有在食堂吃和带走两种可能。对应这种情况,我们一般会先想到这样的解决方法:
public abstract class Meal
{
public abstract void Heat();
public abstract void PutInContainer();
}
public class Meal_A:Meal
{
public override void Heat()
{
//do sth
}
public override void PutInContainer()
{
//do sth
}
}
public class Meal_B:Meal
{
public override void Heat()
{
//do sth
}
public override void PutInContainer ()
{
//do sth
}
}
public class Meal_A_ForSendOut:Meal_A
{
public override void Heat()
{
//do sth
}
public override void PutInContainer ()
{
//do sth
}
}
public class Meal_A_ForRestaurant:Meal_A
{
public override void Heat()
{
//do sth
}
public override void PutInContainer ()
{
//do sth
}
}
public class Meal_B_ForSendOut:Meal_B
{
public override void Heat()
{
//do sth
}
public override void PutInContainer ()
{
//do sth
}
}
public class Meal_B_ForRestaurant:Meal_B
{
public override void Heat()
{
//do sth
}
public override void PutInContainer ()
{
//do sth
}
}

这里我们先创建一个虚基类Meal,两种套餐Meal_A和Meal_B都继承自这个基类。因为又有在食堂吃和带走两种可能,对于加热和装盘来说又会有不同之处,比如带走的要加热的热一些,用方便饭盒来装等等。因此每种套餐又分为两种。而且对于每一种套餐来说,可能都需要写大量的重复代码。最重要的是如果我们现在又有了一种新的可能性,例如饭在食堂吃菜带走 (对于这个例子我也很无奈,举得不是那么好…),那么对于每一种套餐,我们都要给它加上这么一种可能性,工作量是十分巨大的。
之所以会造成这种问题的主要原因是Meal可以在两个维度上发生变化:套餐和吃法。我们的最终目的就是要将抽象和实现部分分离,因此我们也要将点餐和吃法分离。
public abstract class MealPlatformImplementation
{
public abstract void DoHeat();
public abstract void DoPutInContainer();
}
我们设计一个虚基类MealPlatformImplementation来做不同的吃法的基类。下面列出了两种吃法,它们继承自基类,并分别在加热和装盘上做一些工作。
public class Meal_ForSendOut_Imp:MealPlatformImplementation
{
public override void DoHeat()
{
Console.WriteLine("out heat");
}
public override void DoPutInContainer()
{
Console.WriteLine("out put");
}
}
public class Meal_ForRestaurant_Imp:MealPlatformImplementation
{
public override void DoHeat()
{
Console.WriteLine("Restaurant heat");
}
public override void DoPutInContainer()
{
Console.WriteLine("Restaurant put");
}
}

public abstract class Meal
{
protected MealPlatformImplementation mealImp;
public Meal(MealPlatformImplementation mealImp)
{
this.mealImp = mealImp;
}
public abstract void Heat();
public abstract void PutInContainer();
}

构造函数Meal引入一个MealPlatformImplementation型参数,对基类中的MealPlatformImplementation型对象进行初始化。两种套餐继承自此基类。
public class Meal_A:Meal
{
public Meal_A(MealPlatformImplementation mealImp):base(mealImp)
{
}
public override void Heat()
{
mealImp.DoHeat();
}
public override void PutInContainer()
{
mealImp.DoPutInContainer();
}
}
public class Meal_B:Meal
{
public Meal_B(MealPlatformImplementation mealImp):base(mealImp)
{
}
public override void Heat()
{
mealImp.DoHeat();
}
public override void PutInContainer()
{
mealImp.DoPutInContainer();
}
} 现在Meal系列已经不用关心顾客吃法的问题了,那个工作已经转移到MealPlatformImplementation类中完成了,此时抽象和实现就实现了分离。
我们可以试着使用一下我们的设计
MealPlatformImplementation newMealImp = new Meal_ForRestaurant_Imp();
Meal newMeal = new Meal_A(newMealImp);
newMeal.Heat();
newMeal.PutInContainer();
这样我们就完成了一个在食堂吃的A餐的工作了,当我们要改变吃法的时候我们可以改变MealPlatformImplementation,而改变套餐时候改变Meal,不会因为顾客改变吃法而使Meal都发生改变。而且现在如果出现第三种吃法,我们只要添加一个继承自MealPlatformImplementation的新类就可以了,对于所有套餐不必全部特殊处理。
参考 MSDN WebCast C#设计模式纵横谈 李建忠
浙公网安备 33010602011771号