天天看點

拒絕裝飾模式

拒絕裝飾模式

裝飾模式是委托的一種,它更注重的是對原有對象功能的擴充。是以是一個使用頻率較高的模式。但是!今天我要告訴你,在一些時候,你應該拒絕它的“誘惑”

==============

背景

通常,在我們的程式中都會存在兩類javaBean:Entity(實體類)與Dto(資料傳輸類)。

其中Entity是最普通的javaBean,隻有屬性與setter/getter方法,通常作為資料持久化操作。

而Dto負責應用的業務邏輯中的資料傳遞,除屬性以外,還會有一些業務邏輯方法。通常,Dto是Entity的擴充。

實際的使用情況是什麼呢?我們在開發中并沒有将二者區分開,而是合二為一,以Entity為基礎,在上面添加額外的屬性和方法。這樣做有什麼好處呢?

好處:

  • 不需要維護兩套類
  • Entity與Dto有着高度相似性,合二為一後不需要寫重複的代碼。

缺點:

  • 一個本應該“幹淨”的javaBean混入了過多的業務邏輯,從Entity的角度,這已經不單一了。
  • Entity、Dto合二為一的後果就是你得到了一個臃腫的Dto
  • 在使用了ORM架構的情況下,為了區分出需要持久化和不需要持久化的屬性,不得不又引入了注解。
  • 一個永遠沒有答案的問題:javaBean裡究竟應不應該有計算方法?

那麼分析完優缺點後,接下來,我們嘗試将二者拆開。

拆開絕對不是簡單拆除兩套類,這是非常愚蠢的。

既然Dto是Entity的擴充,那麼很快我就想到了裝飾模式。功能擴充呀,沒錯的。

嘗試

這裡假設你已經對裝飾模式比較了解了。

第一步:建立,如果使用裝飾模式,Entity和Dto就應該有一套相同的接口。

額……javaBean的接口?難道你是說getter和setter方法? 哦~哪個傻蛋會這麼做……

姑且我們傻了一回,這樣做了。第二步:使用,在使用Dto的過程中,你需要再new一個Dto裝飾者。然後将Entity的對象設定進去,就像這樣:

//通過manager取得了user對象
UserEntity userEntity = userManager.getUser();

//要使用Dto了
IUser userDto = new UserDto(userEntity);
....
           

我不知道你怎麼看這個寫法,我覺得有點蠢。

我為自己規定的代碼優化與重構守則:

  • 在不增加原有代碼編寫複雜度的情況進行優化。

以前我就是看着難受了一點,現在你這麼一搞,直接給我增加勞動量了,我肯定不願意!!!

就這麼走了兩步就走不下去了~怎麼辦吧你說……

拒絕裝飾模式

說到功能擴充,你除了裝飾模式,還能想到什麼?繼承!!

比較喜劇性的是,在設計模式之禅這本書中,襯托裝飾模式的就是繼承。

繼承的缺點有哪些呢?當需要擴充的情況變多以後,容易造成類爆炸。多層繼承結構維護困難等等~然後呢?其實有時候你會發現,這些對你來說并不是問題。

比學會設計模式更重要的事情是知道什麼時候該使用設計模式!

你真的有很多擴充的可能嗎?NO~至少我開發Androd的兩年以來并沒有遇到這樣的需求。你真的有很多繼承結構嗎?NO~除了android native元件,真的很少會遇到超兩層繼承結構的東西。

既然繼承的缺點不是缺點了,那麼你可以理直氣壯的拒絕裝飾模式了嗎?

來,我們直接上代碼。

案例:紀念日類

MemorialDayEntity.java 實體類。用于資料持久化的一些基本屬性。

public class MemorialDayEntity {

    protected int id;
    protected String title;
    protected String datetime;
    protected String buildName;
    protected String buildAccount;
    protected boolean loop;

    public MemorialDayEntity(){}

    public MemorialDayEntity(String buildAccount, String buildName, String datetime, int id, boolean loop, String title) {
        this.buildAccount = buildAccount;
        this.buildName = buildName;
        this.datetime = datetime;
        this.id = id;
        this.loop = loop;
        this.title = title;
}

    /**
    * getter and setter
    */

}
           

MemorialDayDto.java Dto類。在Entity的基礎上增加了許多需要臨時計算的屬性:在重複類型與不重複類型下與目前日期的內插補點,用于顯示的組合名稱等等。

按時
public class MemorialDayDto extends MemorialDayEntity {
    private static final DateFormat DATE_FORMAT = SimpleDateFormat.getDateInstance();
    private Calendar calendar = GregorianCalendar.getInstance();

    private Date nowDate;
    private Date targetDate;
    private int dateYear;
    private int dateMonth;
    private int dateDay;
    private String nameStr;
    private long day;

    public MemorialDayDto(String buildAccount, String buildName, String datetime, int id, boolean loop, String title) {
    super(buildAccount, buildName, datetime, id, loop, title);
    try {
        nowDate = new Date();
        targetDate = DATE_FORMAT.parse(datetime);
        calendar.setTime(targetDate);
        dateYear = calendar.get(Calendar.YEAR);
        dateMonth = calendar.get(Calendar.MONTH);
        dateDay = calendar.get(Calendar.DATE);
        calculateDate();
    } catch (ParseException e) {
        e.printStackTrace();
    }
}

    public String getDateStr() {
    return dateYear + "年" + dateMonth + "月" + dateDay + "日";
}

    private void calculateDate() {
        //日期計算
    }
}
           

兩個類我們設計完了。要怎麼用呢?

在之前二者合二為一的項目代碼基礎上,不需要做任何修改,還是直接使用Dto類。

不同的是在做資料儲存時。這個時候也是非常簡單的。看代碼:

Dao層資料儲存方法:

public interface MemorialDayDao{

    public void save(MemorialDayEntity entity);

    ....
}

class Client{

    MemorialDayDto memorialDayDto = //一些列的操作後我們得到了紀念日Dto類

    MemorialDayDao mDao = Dao.getMemorialDayDao();

    //就這樣,這裡也幾乎沒有任何改變
    mDao.save(memorialDayDto);

}
           

發現我們的改變了嗎?就是将需要持久化的屬性抽出放到父類中當做Entity。

為什麼在資料儲存時,Dao層需要的是Entity,我們傳Dto确沒有問題呢?你難道忘啦?這可是正經的裡氏替換啊:父類出現的地方,子類都可以無縫替換。

沒錯,就是無縫,相對于裝飾模式的改變,使用了繼承,就實作了無縫的代碼優化。爽不爽???

裝飾模式的優點

雖然我們拒絕了裝飾模式,但還是要知道它的優點的

  • 裝飾模式的特點是動态。這是對比繼承非常大的一個特點
  • 你可以定義非常多的裝飾類,這些裝飾類的維護成本較繼承的成本是較低裡的。
  • 裝飾模式的擴充和原本的基礎體是分開解耦的

裝飾模式的缺點

  • 多層裝飾和多層繼承一樣,非常容易帶來維護困難的問題。
  • 裝飾模式并不是絕對的優于繼承。這需要充分考慮使用場景,就像我們這篇文章所講的。

繼續閱讀