前言 :
您打算設計一個點餐程式,目前主餐有炸雞、漢堡,您打算讓點了主餐的客入選擇附餐時可以有優惠,如果您使用繼承的方式來達到這個目的,例如:
- class FriedChicken {
- double price() {
- return 49.0;
- }
- }
- class SideDishOne extends FriedChicken {
- double price() {
- return super.price() + 30.0;
- }
- }
建議解法 :
如果採取以下的設計,可以解決問題:
這個設計不採取繼承而改以組合的方式,如果以Java來示範如何為不同的主餐增加附餐的話,如下所示:
* Meal 介面代碼 :
- package ch06.order.proto;
- public interface Meal {
- public String getContent();
- public double getPrice();
- }
- package ch06.order.proto;
- public abstract class AbstractSideDish implements Meal{
- protected Meal meal;
- public AbstractSideDish(Meal m){
- this.meal = m;
- }
- }
- package ch06.order.food;
- import ch06.order.proto.*;
- public class FriedChicken implements Meal{
- private String content;
- private double price;
- public FriedChicken(String name, double price){
- this.content = name;
- this.price = price;
- }
- @Override
- public String getContent() {
- return content;
- }
- @Override
- public double getPrice() {
- return price;
- }
- }
- package ch06.order.food;
- import ch06.order.proto.AbstractSideDish;
- import ch06.order.proto.Meal;
- public class SideDishOne extends AbstractSideDish{
- public SideDishOne(Meal m){
- super(m);
- }
- @Override
- public String getContent() {
- return meal.getContent()+" | 可樂 | 薯條";
- }
- @Override
- public double getPrice() {
- return meal.getPrice() + 30;
- }
- }
- package ch06.order;
- import ch06.order.food.*;
- import ch06.order.proto.*;
- public class Main {
- public static void main(String args[]) {
- Meal sideDishMeal = new SideDishOne(new FriedChicken("好吃炸雞", 100));
- System.out.println("Content: " + sideDishMeal.getContent());
- System.out.println("Price : " + sideDishMeal.getPrice());
- }
- }
這是Decorator模式的實現,其不採取繼承的方式,而以組合的方式動態地為物件添加功能。
執行結果 :
Decorator 設計模式介紹 :
通過使用修飾模式,可以在運行時擴充一個類的功能。原理是:增加一個修飾類包裹原來的類,包裹的方式一般是通過在將原來的對象作為修飾類的構造函數的參數。裝飾類實現新的功能,但是,在不需要用到新功能的地方,它可以直接調用原來的類中的方法。修飾類必須和原來的類有相同的介面。
修飾模式是類繼承的另外一種選擇。類繼承在編譯時候增加行為,而裝飾模式是在運行時增加行為。
當有幾個相互獨立的功能需要擴充時,這個區別就變得很重要。在有些物件導向的程式語言中,類不能在運行時被創建,通常在設計的時候也不能預測到有哪幾種功能組合。這就意味著要為每一種組合創建一個新類。相反,修飾模式是面向運行時候的對象實例的,這樣就可以在運行時根據需要進行組合。一個修飾模式的示例是JAVA里的Java I/O Streams的實現。
底下以UML來表示Decorator模式之結構:
在Java Swing中的JTextArea元件預設並沒有捲軸,捲軸的功能是由JScrollPane元件提供,如果您要加入一個具有捲軸功能的JTextArea,您可以如下進行設計:
- JTextArea textArea = new JTextArea();
- JScrollPane scrollPane = new JScrollPane(textArea);
在Gof的書中指出另一個範例,它設計一個Stream抽象類,而有一個StreamDecorator類,Stream的子類有處理記憶體串流的 MemoryStream與FileStream,有各種方法可以處理串流,也許只是單純的處理字元,也許會進行壓縮,也許會進行字元轉換,最基本的處理可能是處理字元,而字元壓縮被視為額外的功能,這個時候我們可以使用裝飾模式,在需要的時候為Stream物件加上必要的功能,事實上在java.io中的許多輸入輸出物件,就是採取這樣的設計,例如:
- BufferedReader reader = new BufferedReader(new FileReader("Main.java"));
FileReader 沒有緩衝區處理的功能,所以由BufferedReader來提供,BufferedReader並沒有改變FileReader的功能,而是在既有 FileReader的操作上再作加工的動作,而BufferedReader也不只可以用於FileReader,只要是Reader的子類別,都可以套用BufferedReader,例如讀取使用者輸入時:
- BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
補充說明 :
* Wiki Decorator 修飾模式
* 史帝芬心得筆記-- Decorator Pattern (裝飾模式)
沒有留言:
張貼留言