|
Design Pattern - Decorator
|
| (史帝芬, 2007/04/23, hi.steven@gmail.com) |
試著想一下,如果我們要寫一個牛排館的計費程式,這家牛排館有兩樣主菜及一些附餐,
主菜為牛排、豬排,附餐有麵、生菜沙拉、飲料、甜點,客人點菜時,需點一樣主菜,附
餐可多選也可不點,計費的方式就像自助餐一樣,每樣菜有它的價格,相加後即為客人要
付的費用。我們希望未來這家牛排館增加主菜或附餐時,都不需要修改既有程式! 可能嗎?
下面我們來看看Decorator Pattern如何滿足這樣的需求!
using System;
namespace Decorator
{
abstract class Meal
{
protected string description = "排餐";
public virtual string Description
{
get { return description; }
set { description = value; }
}
abstract public int cost();
}
class PorkChop : Meal
{
public PorkChop()
{
Description = "豬排";
}
public override int cost()
{
return 130;
}
}
class Steak : Meal
{
public Steak()
{
Description = "牛排";
}
public override int cost()
{
return 150;
}
}
abstract class Condiment : Meal
{
protected Meal meal;
public override string Description
{
get { return meal.Description + " + " + description; }
set { description = value; }
}
}
class Noodle : Condiment
{
public Noodle(Meal meal)
{
Description = "麵";
this.meal = meal;
}
public override int cost()
{
return 50 + meal.cost();
}
}
class Salad : Condiment
{
public Salad(Meal meal)
{
Description = "生菜沙拉";
this.meal = meal;
}
public override int cost()
{
return 60 + meal.cost();
}
}
class Drink : Condiment
{
private bool isHot;
public bool IsHot
{
get { return isHot; }
}
public Drink(Meal meal, bool isHot)
{
Description = (isHot) ? "熱飲" : "冷飲";
this.meal = meal;
this.isHot = isHot;
}
public override int cost()
{
return (isHot) ? (30 + meal.cost()) : (50 + meal.cost());
}
}
class Dessert : Condiment
{
public Dessert(Meal meal)
{
Description = "甜點";
this.meal = meal;
}
public override int cost()
{
return 40 + meal.cost();
}
}
class Program
{
static void Main(string[] args)
{
Meal meal = new PorkChop();
Console.WriteLine(meal.Description + " = NT$ " + meal.cost());
Meal meal2 = new Noodle(meal);
Console.WriteLine(meal2.Description + " = NT$ " + meal2.cost());
Meal meal3 = new Drink(meal2, true);
Console.WriteLine(meal3.Description + " = NT$ " + meal3.cost());
Meal meal4 = new Dessert(meal3);
Console.WriteLine(meal4.Description + " = NT$ " + meal4.cost());
Console.ReadLine();
}
}
}
豬排 = NT$ 130 豬排 + 麵 = NT$ 180 豬排 + 麵 + 熱飲 = NT$ 210 豬排 + 麵 + 熱飲 + 甜點 = NT$ 250 由上面的程式可以了解,未來要增加主菜,只要寫一個和牛排、豬排一樣的class繼承Meal ; 要增加附餐,則如飲料、生菜沙拉一樣,寫個繼承Condiment的class即可。這個Pattern最有趣的是,Condiment既繼承了 Meal,也合成Meal,其實這裡的繼承只是為了讓配菜和主菜同型別,功能上是用合成取得的。在物件導向 程式設計裡,為了降低兩個類別間的耦合度,使用合成取代繼承是常見的手法。 在Java I/O裡也可以看到Decorator Pattern的運用。 在研究這個Pattern時,感謝Michael Tsai解答了我心中的疑惑, Microsoft Forum。 |