拉麵的故事
拉麵館裡賣拉麵,拉麵分成小碗和大碗,小碗一份6元,大碗一份9元。另外如果加牛肉的話,則需加6元,加一個雞蛋是1元,加大排是5元一份,加一塊鍋巴是1元。如果用傳統的寫法,設定不同價格的拉麵,需要寫8個類別(拉麵份量數*配菜數)。如果現在麵館新推一種份量——中碗,那麼,就需要新增4個類別。這樣就會造成一個問題──類爆炸。
如果你看過我之前的文章https://www.php.cn/php-weizijiaocheng-457250.html,了解了橋接模式後,會覺得這個問題可以用橋接模式來解決。把它分為兩個大類,麵條和配菜。
下面我們用橋接模式來完成上述問題,程式碼如下:
interface INoodle { function cost (); function desc (); } class BigNoodle implements INoodle { private $cost = 9.0; private $dish = null; public function __construct(IDish $dish) { $this->dish = $dish; } public function cost() { return $this->cost + $this->dish->cost(); } public function desc() { return $this->dish->desc() . '大碗拉面'; } } class SmallNoodle implements INoodle { private $cost = 6.0; private $dish = null; public function __construct(IDish $dish) { $this->dish = $dish; } public function cost() { return $this->cost + $this->dish->cost(); } public function desc() { return $this->dish->desc() . '小碗拉面'; } } interface IDish { function cost (); function desc (); } class Beef implements IDish { public function cost () { return 6; } public function desc() { return '牛肉'; } } class Crust implements IDish { public function cost () { return 1; } public function desc() { return '锅巴'; } } class Egg implements IDish { public function cost () { return 1; } public function desc() { return '鸡蛋'; } }
#裝飾模式
##使用橋接模式確實解決了類爆炸問題,但你也知道,我們去吃麵,可能有時候不要配菜,只要面,又或者我們需要多個配菜,比如,我要份大碗牛肉拉麵,加3塊鍋巴以及2顆雞蛋。對於這種需求,使用橋接模式是完成不了的。想要解決這種問題,我們可以藉助另一種結構型設計模式──裝飾者模式。裝飾模式是一種結構型設計模式, 允許你透過將物件放入包含行為的特殊封裝物件中來為原始物件綁定新的行為。
想要理解裝飾者模式,可以想像一個玩偶-套娃#每套一個娃,就相當於加了一個裝飾的物件。在運行時,會運行最外層的裝飾物件(取外層的娃),然後一層一層的運行。現在你可能不懂什麼意思,看完後面的內容再來會看這句話或許就會明白。 我自己畫了個uml類別圖,有點醜,大家將就點程式碼實作
abstract class Noodles { abstract function cost (); abstract function desc (); } class BigNoodle extends Noodles { private $cost = 9.0; public function cost() { return $this->cost; } public function desc() { return '大碗拉面'; } } class SmallNoodle extends Noodles { private $cost = 6.0; public function cost() { return $this->cost; } public function desc() { return '小碗拉面'; } } abstract class NoodlesDecorator extends Noodles { } class Beef extends NoodlesDecorator { private $desc = '牛肉'; private $cost = 6.0; protected $noodles = null; public function __construct(Noodles $noodels) { $this->noodles = $noodels; } public function cost () { return $this->cost + $this->noodles->cost(); } public function desc () { return $this->desc . $this->noodles->desc(); } } // egg、curst类代码省略,除了属性值不一样基本和Beef一致
$noodles = new BigNoodle(); $beefBigNoodles = new Beef($noodles); $eggBeffBigNoodles = new Egg($beefBigNoodles); echo $eggBeffBigNoodles->desc(); echo $eggBeffBigNoodles->cost() . '元';
##總結 思考一個問題,為什麼這裡沒有把拉麵的份量當作裝飾者物件?想想看,你會點一份既是大碗又是小碗的拉麵嗎?
裝飾者模式特點
以上是什麼是裝飾者模式,它與橋接模式有什麼不同?的詳細內容。更多資訊請關注PHP中文網其他相關文章!