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接口的炒粉類實現對應的方法就可以了,是不是很方便,很好擴展,還不用修改之前的