亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

工廠模式

工廠模式是平時開發過程中最常見的設計模式。工廠模式解決類的實例化問題,它屬于創建型模式。工廠模式也經常會和其他設計模式組合使用。

試想你去麥當勞買一個漢堡。你只需要告訴收銀員要一個xx漢堡。過一會就會有一個此類型的漢堡被制作出來。而你完全不需要知道這個漢堡是怎么被制作出來的。這個例子中你就是客戶端代碼,麥當勞就是工廠,負責生產漢堡。漢堡是接口,而具體的某一種漢堡,比如說香辣雞腿堡,就是實現了漢堡接口的類。

我們繼續通過另外一個例子,深入理解工廠模式?,F在我們給某款音樂軟件開發一個推薦功能。需求是能夠根據用戶選擇的音樂風格,推薦不同風格的歌曲清單。那么你打算怎么實現呢?

1. 音樂推薦器1.0版本

如果之前沒有學習過設計模式,很可能你的實現會是這樣。編寫 RecommendMusicService 類,里面有一個 Recommend方法。根據輸入的風格不同,執行不同的推薦邏輯。代碼如下:

public class RecommendMusicService {
    public List<String> recommend(String style) {
        List<String> recommendMusicList = new ArrayList<>();

        if ("metal".equals(style)) {
            recommendMusicList.add("Don't cry");
        } else if ("country".equals(style)) {
            recommendMusicList.add("Hotel california");
        } else if ("grunge".equals(style)) {
            recommendMusicList.add("About a girl");
        }else {
            recommendMusicList.add("My heart will go on");
        }

        return recommendMusicList;
    }
}

是不是覺得 recommed 方法太長了? OK,我們重構下,把每種音樂風格的推薦邏輯封裝到相應的方法中。這樣推薦方法就可以復用了。

public class RecommendMusicService {
    public List<String> recommend(String style) {
        List<String> recommendMusicList = new ArrayList<>();

        if ("metal".equals(style)) {
            recommendMetal(recommendMusicList);
        } else if ("country".equals(style)) {
            recommendCountry(recommendMusicList);
        } else if ("grunge".equals(style)) {
            recommendGrunge(recommendMusicList);
        }else {
            recommendPop(recommendMusicList);
        }

        return recommendMusicList;
    }

    private void recommendPop(List<String> recommendMusicList) {
        recommendMusicList.add("My heart will go on");
        recommendMusicList.add("Beat it");
    }

    private void recommendGrunge(List<String> recommendMusicList) {
        recommendMusicList.add("About a girl");
        recommendMusicList.add("Smells like teen spirit");
    }

    private void recommendCountry(List<String> recommendMusicList) {
        recommendMusicList.add("Hotel california");
        recommendMusicList.add("Take Me Home Country Roads");
    }

    private void recommendMetal(List<String> recommendMusicList) {
        recommendMusicList.add("Don't cry");
        recommendMusicList.add("Fade to black");
    }
}

這樣是不是很完美了!recommend 方法精簡了很多,而且每種不同的推薦邏輯都被封裝到相應的方法中了。那么,如果再加一種風格推薦怎么辦?這有什么難,recommed 方法中加分支就好啦。然后在 RecommendMusicService 中增加一個對應的推薦方法。
等等,是不是哪里不太對?回想一下設計模式6大原則的開閉原則----對擴展開放,對修改關閉。面對新風格推薦的需求,我們一直都在修改 RecommendMusicService 這個類。以后每次有新風格推薦要添加,都會導致修改 RecommendMusicService 。顯然這是個壞味道。

那么如何做到實現新的風格推薦需求時,滿足開閉原則呢?

2. 音樂推薦器2.0版本

添加新需求時,如何做到不修改,去擴展?是不是想到了單一職責?是的,類的職責越單一,那么它就越穩定。RecommendMusicService 類的職責太多了,負責n種風格的推薦。OK,那么我們第一件事就是要減少 RecommendMusicService 類的職責,把每種不同風格的推薦提取到不同的類當中。
比如MetalMusicRecommendService、PopMusicRecommendService、CountryMusicRecommendService。這些類都可以通過 recommed 方法生成推薦的歌曲清單。而 RecommendMusicService 類只是通過調用不同 MusicRecommendService 的 recommed 方法來實現推薦。代碼如下:

MetalMusicRecommendService 類:

public class MetalMusicRecommendService {
    public List<String> recommend(){
        List<String> recommendMusicList = new ArrayList<>();

        recommendMusicList.add("Don't cry");
        recommendMusicList.add("Fade to black");

        return recommendMusicList;
    }
}

同類型的還有 GrungeMusicRecommendService、PopMusicRecommendService、CountryMusicRecommendService

現在我們來改造 MusicRecommendService 類:

public class RecommendMusicService {

    private MetalMusicRecommendService metalMusicRecommendService = new MetalMusicRecommendService();
    private GrungeMusicRecommendService grungeMusicRecommendService = new GrungeMusicRecommendService();
    private CountryMusicRecommendService countryMusicRecommendService = new CountryMusicRecommendService();
    private PopMusicRecommendService popMusicRecommendService = new PopMusicRecommendService();

    public List<String> recommend(String style) {
        List<String> recommendMusicList = new ArrayList<>();

        if ("metal".equals(style)) {
            metalMusicRecommendService.recommend();
        } else if ("country".equals(style)) {
            countryMusicRecommendService.recommend();
        } else if ("grunge".equals(style)) {
            grungeMusicRecommendService.recommend();
        }else {
            popMusicRecommendService.recommend();
        }

        return recommendMusicList;
    }

}

改造后,如果有了新音樂風格推薦的需求,只需要增加相應的 xxxMusicRecommendService 類。然后在 RecommendMusicService 中增加相應分支即可。這樣就做到了開閉原則。那么還有什么違背設計原則的地方嗎?RecommendMusicService 是不是依賴的 xxMusicRecommendService 類太多了?

沒錯,而且這么多類,實際上都是做推薦的事情,且都是通過 recommend 方法提供推薦結果。這完全可以抽象出接口,比如 MusicRecommendInterface。那么 RecommendMusicService 依賴 MusicRecommendInterface 就可以了。這解決了依賴反轉問題----應該依賴接口,而不是依賴具體實現

我們又復習了單一職責和依賴反轉原則。不愧是指導設計模式的原則,真的是無處不在。依賴 MusicRecommendInterface 沒問題,但是不同的音樂風格,怎么能實例化 MusicRecommendInterface 的某個具體實現呢?工廠模式于是就應運而生了!

3. 音樂推薦器3.0版本

我們回顧一下文章開頭說到,工廠模式解決的是類的實例化。無論你需要哪種風格的 MusicRecommendService,只需要告訴工廠,工廠會給你實例化好你需要的具體實現。而工廠能做到這些是基于繼承和多態。
RecommendMusicService 只需要依賴 MusicRecommendInterface,具體需要哪個MusicRecommendService 的實現,只需要告訴 RecommendServiceFactory 即可。MusicRecommendService 拿到具體的實現后調用它的 recommand 方法,就可以得到相應風格的推薦歌曲清單。

首先我們需要定義所有 MusicRecommendService 要實現的接口,很簡單,只有一個 recommend 方法:

public interface MusicRecommendInterface {
    List<String> recommend();
}

我們2.0版本中的 xxxMusicRecommendService 都需要實現此接口,例如:

public class GrungeMusicRecommendService implements MusicRecommendInterface {
    public List<String> recommend() {
        List<String> recommendMusicList = new ArrayList<>();

        recommendMusicList.add("About a girl");
        recommendMusicList.add("Smells like teen spirit");

        return recommendMusicList;
    }
}

不同音樂風格的推薦邏輯在各自實現的 recommend() 方法中。
下面就是工廠模式中的工廠代碼了,其實很簡單,只是根據不同的參數實例化不同的實現并返回。

public class MusicRecommendServiceFactory {
    MusicRecommendInterface createMusicRecommend(String style) {
        if ("metal".equals(style)) {
            return new MetalMusicRecommendService();
        } else if ("country".equals(style)) {
            return new CountryMusicRecommendService();
        } else if ("grunge".equals(style)) {
            return new GrungeMusicRecommendService();
        } else {
            return new PopMusicRecommendService();
        }
    }
}

我們再來看看 RecommendMusicService 的代碼:

public class RecommendMusicService {

    private MusicRecommendServiceFactory recommendMusicServiceFactory = new MusicRecommendServiceFactory();

    public List<String> recommend(String style) {

        MusicRecommendInterface musicRecommend = recommendMusicServiceFactory.createMusicRecommend(style);

        return musicRecommend.recommend();
    }
}

是不是簡單多了,已經不再依賴那么多的 MusicRecommendInterface 的實現了。它要做的事情僅僅是通過工廠得到想要的 RecommendMusicService 實現,然后調用它的 recommend() 方法,就可以得到你想要的推薦結果。
類圖如下:
圖片描述
以上三種實現方式總結如下:
圖片描述

4. 小結

本節我們通過音樂推薦器的例子,實踐了如何找到程序中違反設計原則的地方,并通過工廠模式來解決這些問題。使用設計模式可以讓程序更符合程序設計原則,從而寫出更為健壯的代碼。我們應牢記工廠模式解決的是類的實例化問題。這個例子很簡單,不過涉及到的知識點卻很多。有封裝、多態、單一職責和依賴反轉等??梢娨氚殉绦蛟O計好,必須熟練掌握這些基本概念和原則。