Java設計模式之 —— 裝飾者(Decorator) — 點炒飯

Java設計模式之 —— 裝飾者(Decorator) — 點炒飯

下面會用做炒飯的例子來描述一下裝飾者設計模式,裝飾者設計模式比較適合用於拓展功能,我要做炒飯,最簡單的就是油鹽加米飯就可以做出炒飯,但是我們需要用裝飾者的思想做出蛋炒飯,肉絲炒飯,培根炒飯。
要是不用設計模式,我們用java實現這個那就是寫一個類,類裏面放上各種食材(肉絲、雞蛋、培根之類的),在我們給素炒飯添加添加食材的時候就用if語句來判斷。一般來講我們很可能寫成下面這樣的:

 素炒飯
 .
 .
 .
 if(添加 肉絲){
     name: 肉絲炒飯
     price:素炒飯價錢 + 肉絲價錢
 }else if(雞蛋){
     name: 雞蛋炒飯
     price:素炒飯價錢 + 雞蛋價錢
 }else if{
     ...
 }
 .
 .
 .

如果我們需要添加食材,或者一種食材需要添加兩次(放兩份兒肉絲),我們每次都需要去修改類中的代碼,一旦我們的食材種類多了,那就很操蛋了,你們是不是?
這裏寫圖片描述
普通的寫法不利於擴展,有修改或者增加新的食材都需要在類裏面修改代碼。這個時候就該考慮裝飾者設計模式,裝飾者設計模式的宗旨是 “歡迎擴展,拒絕修改” ,下面是Head Frist 設計模式的描述:

裝飾者設計模式:動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。

做炒飯我可以添加食材,但是不需要修改類裏面的代碼,是不是很好?
下面我用裝飾者設計模式來做炒飯,我們先來創建一個主食的超類,不管是炒飯還是炒麪還是炒粉都要實現這個接口,裏面有兩個方法,一個計算name一個計算price。

1.主食接口超類:

/**
 * @Description: 主食接口超類
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 9:11
 */
public interface IFood {
    /**
     * 主食價格
     */
    int caculatePrice();

    /**
     * 主食描述
     */
    String description();

}

2.然後我們給食材也常見一個超類,我們的肉絲、雞蛋、培根都實現這個接口:

/**
 * @Description: 添加食材的超類接口,就是繼承了IFood接口,當
 *               食材實現這個接口的時候需要用到IFood中的方法來對價格和描述做出修改
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 9:31
 */
public interface IFoodDecorator extends IFood {

    //這裏爲了食材添加別的功能也可以增加新的方法,比如食材的功效之類的

}

3.炒飯類:

/**
 * @Description:  素炒飯 價格 5 元
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 9:26
 */
public class ChaoFan implements IFood {
    /**
     * 主食價格
     */
    @Override
    public int caculatePrice() {
        return 5;
    }

    /**
     * 主食描述
     */
    @Override
    public String description() {
        return "炒飯";
    }
}

4.炒麪類

/**
 * @Description:  素炒麪  價格 4 元
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 9:27
 */
public class ChaoMian implements IFood {
    /**
     * 主食價格
     */
    @Override
    public int caculatePrice() {
        return 4;
    }

    /**
     * 主食描述
     */
    @Override
    public String description() {
        return "炒麪";
    }
}

5.肉絲食材類:

/**
 * @Description: 肉絲添加食材
 *               價格: 5 元
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 9:35
 */
public class RoseDecorator implements IFoodDecorator {

    private IFood iFood;  //主食的引用,這樣可以直接對主食的name和price做修改

    /**
     * 在構建食材的時候告訴是用於在什麼主食中,
     * 我們只需要使用IFood接口就可以來修改主食的價格和描述,這就是IFood超類的好處
     * @param food
     */
    public RoseDecorator(IFood food){
        this.iFood = food;
    }

    /**
     * 主食價格
     */
    @Override
    public int caculatePrice() {
        /**在主食本來具有的價格上增加 5 元**/
        return iFood.caculatePrice() + 5;
    }

    /**
     * 主食描述
     */
    @Override
    public String description() {
        /**在主食本來具有的描述上增加 '肉絲' 描述 **/
        return "肉絲 "+iFood.description();
    }
}

6.雞蛋食材類:

/**
 * @Description: 雞蛋食材  價格 3 元
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 9:53
 */
public class EggDecorator implements IFoodDecorator {

    private IFood iFood;

    /**
     * 在構建食材的時候告訴是用於在什麼主食中,
     * 我們只需要使用IFood接口就可以來修改主食的價格和描述,這就是IFood超類的好處
     * @param food
     */
    public EggDecorator(IFood food){
        this.iFood = food;
    }

    /**
     * 修改 主食價格
     */
    @Override
    public int caculatePrice() {
        return iFood.caculatePrice() + 3;
    }

    /**
     * 修改 主食描述
     */
    @Override
    public String description() {
        return "雞蛋 " + iFood.description() ;
    }
}

7.使用

我是做android開發的,我在activity中測試的:

package com.java.ui;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import com.danxx.views.R;
import com.java.decorator.ChaoFan;
import com.java.decorator.ChaoMian;
import com.java.decorator.EggDecorator;
import com.java.decorator.IFood;
import com.java.decorator.RoseDecorator;

/**
 * @Description: 裝飾者設計模式測試
 * @Author: Danxingxi
 * @CreateDate: 2016/10/13 10:00
 */
public class ActivityDecorator extends AppCompatActivity {

    private TextView name,price;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_decorator);
        name = (TextView) findViewById(R.id.name);
        price = (TextView) findViewById(R.id.price);
    }

    /**
     * 做一份肉絲炒飯
     * @return
     */
    public void roseChaoFan(View view){
        IFood iFood = new RoseDecorator(new ChaoFan());
        name.setText("主食: "+ iFood.description());
        price.setText("價格: "+ iFood.caculatePrice() +" 元");
    }

    /**
     * 做一份肉絲雞蛋炒飯
     * @return
     */
    public void roseEggChaoFan(View view){
        IFood iFood = new RoseDecorator(new EggDecorator(new ChaoFan()));
        name.setText("主食: "+ iFood.description());
        price.setText("價格: "+ iFood.caculatePrice() +" 元");
    }

    /**
     * 做一份雞蛋肉絲炒麪
     * @return
     */
    public void eggRoseChaoMian(View view){
        IFood iFood = new EggDecorator(new RoseDecorator(new ChaoMian()));
        name.setText("主食: "+ iFood.description());
        price.setText("價格: "+ iFood.caculatePrice() +" 元");
    }

}

效果:
這裏寫圖片描述

8.總結

源代碼地址:Java設計模式裝飾者

裝飾者模式 對於擴展功能來說很好,比如我們的炒飯,我們需要添加牛肉食材,我們只需新建一個實現了IFoodDecorator 接口的類,並在兩個方法中作出自己的修改就可以了,這樣就把牛肉食材增加了,如果我們要增加炒粉,那就增加一個實現了IFood接口的炒粉類實現對應的方法就可以了,是不是很方便,很好擴展,還不用修改之前的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章