Android常用設(shè)計模式(二)

繼上一篇 Android常用設(shè)計模式(一)里認(rèn)識了觀察者,適配器牛郑,代理等三種模式独令,這一篇將會講解以下三種模式:

  • 工廠模式
  • 單例模式
  • 命令模式

1.工廠模式(Factory Pattern)

工廠模式分為簡單工廠模式吐辙,工廠方法模式以及抽象工廠模式

  • 簡單工廠模式:一般情況下妒蔚,提供一個方法,方法的參數(shù)是一個標(biāo)志位残揉,根據(jù)標(biāo)志位來創(chuàng)建不同的對象胧后,這樣調(diào)用的時候只需要提供一個標(biāo)志位就可以創(chuàng)建一個實現(xiàn)了接口的類。
  • 工廠方法模式:將簡單工廠模式的那個方法分開抱环,不再是在工廠方法中根據(jù)標(biāo)志位創(chuàng)建對象了壳快。而是定義一個工廠接口,然后想創(chuàng)建幾個不同類型的對象(即實現(xiàn)了同一接口的不同java類)镇草,就創(chuàng)建了幾個不同類型的工廠眶痰。也就是創(chuàng)建的對象和創(chuàng)建對象的工廠是一一對應(yīng)的。然后客戶端調(diào)用的時候直接去實例化一個具體的對象工廠梯啤,創(chuàng)建相對應(yīng)的對象竖伯。
  • 抽象工廠模式:其實這個名起的有點不知所云沒有表達出這個模式的特點。其實這個模式就是工廠方法模式的稍微擴展一下而已因宇。工廠方法模式里面七婴,一般一個工廠接口只有一個方法,比如createMouse()羽嫡。然后用實現(xiàn)了這個接口的具體工廠類只能生產(chǎn)鼠標(biāo)本姥。而抽象工廠模式就是一個工廠接口有多個方法,比如createMouse() , createKeyboard() 杭棵。 這樣實現(xiàn)了這個工廠接口的具體工廠類就可以既生產(chǎn)鼠標(biāo)又生產(chǎn)鍵盤。

常見實例:比如android的bitmap中常用的BitmapFactory類氛赐,創(chuàng)建Bitmap對象魂爪,通常使用靜態(tài)工廠方法.
這里主要介紹簡單工廠與工廠方法的區(qū)別:
就以大話模式中小菜跟大鳥舉得雷鋒故事作為題材吧。
LeiFeng類

//雷鋒  
public interface LeiFeng {
    void sweep(); //掃地  
    void wash(); //洗衣  
    void buyrice(); //做飯  
}

Student類

//學(xué)做雷鋒的大學(xué)生  
public class Student implements LeiFeng {

    public void buyrice() {
        System.out.println("大學(xué)生做飯");
    }

    public void sweep() {
        // TODO Auto-generated method stub  
        System.out.println("大學(xué)生掃地");
    }

    public void wash() {
        // TODO Auto-generated method stub  
        System.out.println("大學(xué)生洗衣");
    }

}

Valuator類志愿者

//學(xué)做雷鋒的志愿者  
public class Valuator implements LeiFeng {

    public void buyrice() {
        System.out.println("志愿者做飯");
    }

    public void sweep() {
        // TODO Auto-generated method stub  
        System.out.println("志愿者掃地");
    }

    public void wash() {
        // TODO Auto-generated method stub  
        System.out.println("志愿者洗衣");
    }
}

然后簡單工廠是這么實現(xiàn)的:

//使用簡單工廠  
public class SimpleFactory {

    public static LeiFeng createLeiFeng(String type) {
        if ("大學(xué)生".equals(type)) {
            return new Student();
        } else if ("志愿者".equals(type)) {
            return new Valuator();
        }
        return null;
    }
}

而工廠方法模式中艰管,則多了一個接口去創(chuàng)建不同類型的對象:
Factory類

//工廠方法模式滓侍,工廠接口  
public interface Factory {
    LeiFeng createLeiFengFactory();
}

StudentFactory學(xué)生工廠

//學(xué)生工廠  
public class StudentFactory implements Factory {
    public LeiFeng createLeiFengFactory() {
        return new Student();
    }
}

ValuatorFactory志愿者工廠

//志愿者工廠  
public class ValuatorFactory implements Factory {
    public LeiFeng createLeiFengFactory() {
        return new Valuator();
    }
}

當(dāng)我們實現(xiàn)起來時:

public static void main(String[] args) {

    //簡單工廠模式
    LeiFeng f11 = SimpleFactory.createLeiFeng("大學(xué)生");
    f11.buyrice();
    LeiFeng f22 = SimpleFactory.createLeiFeng("大學(xué)生");
    f22.wash();

    //使用工廠方法模式  
    Factory fac = new StudentFactory();
    LeiFeng f4 = fac.createLeiFengFactory();
    f4.buyrice();
    LeiFeng f5 = fac.createLeiFengFactory();
    f5.wash();
}

這里就要說說為什么要使用工廠方法模式,因為簡單工廠使用起來明顯要方便簡約的多牲芋。從理論的角度來說撩笆,工廠方法模式更符合封閉-開放原則捺球。即對修改封閉對擴展開放。試想后期維護過程中要增加一個種類的對象夕冲,也就是增加對接口的一種實現(xiàn)氮兵,簡單工廠模式就要在switch...case中增加一個case項,無疑是修改了工廠方法歹鱼。如果是jar包模式的泣栈,就要重新發(fā)包了。但是工廠方法模式弥姻,完全不需要更改工廠接口南片,只是新增加一個實現(xiàn)的工廠類即可(如果是jar包模式的,就可以不用重新發(fā)jar包庭敦,讓用jar包的人自己去擴展一個實現(xiàn)了工廠接口的具體工廠類即可)疼进。完全符合封閉-擴展原則。

2.單例模式(Single Pattern)

釋義:

單例模式確保某一個類只有一個實例秧廉,而且自行實例化并向整個系統(tǒng)提供這個實例單例模式颠悬。單例模式只應(yīng)在有真正的“單一實例”的需求時才可使用。

故事理解:

俺有6個漂亮的老婆定血,她們的老公都是我赔癌,我就是我們家里的老公Sigleton,她們只要說道“老公”澜沟,都是指的同一個人灾票,那就是我

常見實例:

數(shù)據(jù)庫創(chuàng)建時使用單例模式,Servlet環(huán)境下共享同一個資源或者對象*

適用場景*:

對于定義的一個類茫虽,在整個應(yīng)用程序執(zhí)行期間只有唯一的一個實例對象刊苍。如Android中常見的Application對象。

單例模式可分為餓漢式濒析,懶漢式等:
(一)餓漢式:其特點是應(yīng)用中尚未需要用到此單一實例的時候即先實例化正什。

public class SingleTon {

    // 靜態(tài)實例變量,直接初始化
    private static SingleTon instance = new SingleTon();

    // 私有化構(gòu)造函數(shù)
    private SingleTon() {

}

    // 靜態(tài)public方法号杏,向整個應(yīng)用提供單例獲取方式
    public static SingleTon getInstance() {
        return instance;
    }

}

(二)懶漢式:其特點是延遲加載婴氮,即當(dāng)需要用到此單一實例的時候,才去初始化此單一實例盾致。

public class SingletonA {

    /**  
     * 單例對象實例  
     */
    private static SingletonA instance = null;

    public static SingletonA getInstance() {
        if (instance == null) { //line 12                            
            instance = new SingletonA(); //line 13    
        }
        return instance;
    }
}

在這里要說下懶漢式主经,因為它具有一定的缺陷,我們可以假設(shè)這樣的場景:兩個線程并發(fā)調(diào)用Singleton.getInstance()庭惜,假設(shè)線程一先判斷完instance是否為null罩驻,既代碼中的line 12進入到line 13的位置。剛剛判斷完畢后护赊,JVM將CPU資源切換給線程二惠遏,由于線程一還沒執(zhí)行l(wèi)ine 13砾跃,所以instance仍然是空的,因此線程二執(zhí)行了new Signleton()操作节吮。片刻之后抽高,線程一被重新喚醒,它執(zhí)行的仍然是new Signleton()操作课锌。

所以對它進行了改良:

public class SingletonB {

    /**  
     * 單例對象實例  
     */
    private static SingletonB instance = null;

    public synchronized static SingletonB getInstance() {
        if (instance == null) { //line 12                            
            instance = new SingletonB(); //line 13    
        }
        return instance;
    }
}

往方法上加了個同步鎖厨内,這樣就可以保證不會出線程問題了,但是這里有個很大(至少耗時比例上很大)的性能問題渺贤。除了第一次調(diào)用時是執(zhí)行了SingletonB的構(gòu)造函數(shù)之外雏胃,以后的每一次調(diào)用都是直接返回instance對象。返回對象這個操作耗時是很小的志鞍,絕大部分的耗時都用在synchronized修飾符的同步準(zhǔn)備上瞭亮,因此從性能上說很不劃算。

所以又進行了改進:

public class SingletonC {
    /**  * 單例對象實例  */
    private static SingletonKerriganD instance = null;
    public static SingletonC getInstance() {
        if (instance == null) {
            synchronized(SingletonC.class) {
                if (instance == null) {
                    instance = new SingletonC();
                }
            }
        }
        return instance;
    }
}

目前我用的版本也就是這種固棚。然而统翩,網(wǎng)上有人又對這種單例模式進行了改進,因為還是存在缺陷此洲,具體可以去網(wǎng)上拓展下厂汗。再說說餓漢式的寫法,這種寫法不會出現(xiàn)并發(fā)問題呜师,在ClassLoader加載類后實例就會第一時間被創(chuàng)建娶桦。但餓漢式的創(chuàng)建方式在一些場景中將無法使用:譬如實例的創(chuàng)建是依賴參數(shù)或者配置文件的,在getInstance()之前必須調(diào)用某個方法設(shè)置參數(shù)給它汁汗,那樣這種單例寫法就無法使用了衷畦。

3.命令模式(Command Pattern)

釋義:

把一個請求或者操作封裝到一個對象中。命令模式把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開知牌,委派給不同的對象祈争。命令模式允許請求的一方和發(fā)送的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口角寸,更不必知道請求是怎么被接收菩混,以及操作是否執(zhí)行,何時被執(zhí)行以及是怎么被執(zhí)行的袭厂。

故事理解

俺有一個MM家里管得特別嚴(yán)墨吓,沒法見面,只好借助于她弟弟在我們倆之間傳送信息纹磺,她對我有什么指示,就寫一張紙條讓她弟弟帶給我亮曹。這不橄杨,她弟弟又傳送過來一個COMMAND秘症,為了感謝他,我請他吃了碗雜醬面式矫,哪知道他說:“我同時給我姐姐三個男朋友送COMMAND乡摹,就數(shù)你最小氣,才請我吃面采转。

常見實例

常用的Runnable(在java.lang包下)聪廉,其實就是用了命令模式,具體的體現(xiàn)過程故慈,可見該博客Runnable下的命令設(shè)計模式**

適用場景**:

  • 命令的發(fā)送者和命令執(zhí)行者有不同的生命周期板熊。命令發(fā)送了并不是立即執(zhí)行。
  • 命令需要進行各種管理邏輯察绷。
  • 需要支持撤消\重做操作(這種狀況的代碼大家可以上網(wǎng)搜索下干签,有很多,這里不進行詳細(xì)解讀)拆撼。

其實經(jīng)典的命令模式包括4個角色:
Command:定義命令的統(tǒng)一接口
ConcreteCommand:Command接口的實現(xiàn)者容劳,用來執(zhí)行具體的命令,某些情況下可以直接用來充當(dāng)Receiver闸度。
Receiver:命令的實際執(zhí)行者
Invoker:命令的請求者竭贩,是命令模式中最重要的角色句惯。這個角色用來對各個命令進行控制雨膨。

命令模式

接下來,就以小菜大鳥去燒烤店购啄,給服務(wù)員報菜睁宰,然后服務(wù)員通知廚師為例子肪获。
Command類

/* 
 * 抽象命令 
 */
abstract class Command {
    protected Barbecuer barbecuer;

    public Command(Barbecuer barbecuer) {
        this.barbecuer = barbecuer;
    }

    // 執(zhí)行命令  
    public abstract void excuteCommand();
}

(Receiver類)Barbecuer:

/* 
 * 烤肉串者 
 */
class Barbecuer {

    // 烤羊肉串  
    public void bakeMutton() {
        System.out.println("烤羊肉串!");
    }

    // 烤雞翅  
    public void bakeChickenWing() {
        System.out.println("烤雞翅!");
    }
} 

(ConcreteCommand類)BakeMuttonCommand、BakeChickenWingCommand

/* 
 * 烤羊肉串命令 
 */
class BakeMuttonCommand extends Command {

    public BakeMuttonCommand(Barbecuer barbecuer) {
        super(barbecuer);
    }

    @Override public void excuteCommand() {
        barbecuer.bakeMutton();
    }

    @Override public String toString() {
        return "命令模式柒傻,烤羊肉串命令孝赫!";
    }

}

/* 
 * 烤雞翅命令 
 */
class BakeChickenWingCommand extends Command {

    public BakeChickenWingCommand(Barbecuer barbecuer) {
        super(barbecuer);
    }

    @Override public void excuteCommand() {
        barbecuer.bakeChickenWing();
    }

    @Override public String toString() {
        return "命令模式,烤雞翅命令红符!     ";
    }
}  

**(Invoker類)Waiter **:

/* 
 * 服務(wù)員 
 */
class Waiter {
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private List < Command > orders = new ArrayList < Command > ();

    // 設(shè)定訂單  
    public void setOrder(Command command) {
        orders.add(command);
        System.out.println("增加訂單:" + command.toString() + " \t時間:" + simpleDateFormat.format(new Date()));
    }

    // 取消訂單  
    public void cancelOrder(Command command) {
        orders.remove(command);
        System.out.println("取消訂單:" + command.toString() + " \t時間:" + simpleDateFormat.format(new Date()));
    }

    // 通知全部執(zhí)行  
    public void notifyA() {
        for (Command command: orders) {
            command.excuteCommand();
        }
    }
} 

Client類實現(xiàn)(如下)

public class CommandMode {
    public static void main(String[] args) {
        // 開店前準(zhǔn)備  
        Barbecuer boy = new Barbecuer();
        Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
        Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
        Command bakeChickenWingCommand = new BakeChickenWingCommand(boy);
        Waiter girl = new Waiter();

        // 開門營業(yè)  
        girl.setOrder(bakeMuttonCommand1);
        girl.setOrder(bakeMuttonCommand2);
        girl.setOrder(bakeChickenWingCommand);

        // 點菜完畢青柄,通知廚房  
        girl.notifyA();
    }
}

總結(jié)來說,命令模式是將功能提升到對象來操作预侯,以便對多個功能進行一系列的處理以及封裝致开。
這里要建議下命令模式的使用,當(dāng)我們不清楚是否需要使用時萎馅,一般不用著急去實現(xiàn)它双戳。事實上,在需要的時候通過重構(gòu)實現(xiàn)這個模式并不困難糜芳,只有在真正需要如撤銷/恢復(fù)操作等功能時飒货,把原來的代碼重構(gòu)為命令模式才有意義魄衅。

最后,設(shè)計模式的運用塘辅,有助于代碼的維護與拓展晃虫。任何模式的出現(xiàn),都是為了解決一些特定的場景的耦合問題扣墩,以達到對修改封閉哲银,對擴展開放的效果

六種模式呻惕,學(xué)習(xí)它們荆责,提高自己!除了這六種蟆融,還有幾種比較常用的草巡,接下來會繼續(xù)思考,繼續(xù)熟悉型酥!

如果覺得此文不錯山憨,麻煩幫我點下“喜歡”。么么噠弥喉!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末郁竟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子由境,更是在濱河造成了極大的恐慌棚亩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虏杰,死亡現(xiàn)場離奇詭異讥蟆,居然都是意外死亡,警方通過查閱死者的電腦和手機纺阔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門瘸彤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笛钝,你說我怎么就攤上這事质况。” “怎么了玻靡?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵结榄,是天一觀的道長。 經(jīng)常有香客問我囤捻,道長臼朗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮依溯,結(jié)果婚禮上老厌,老公的妹妹穿的比我還像新娘瘟则。我一直安慰自己黎炉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布醋拧。 她就那樣靜靜地躺著慷嗜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丹壕。 梳的紋絲不亂的頭發(fā)上庆械,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音菌赖,去河邊找鬼缭乘。 笑死,一個胖子當(dāng)著我的面吹牛琉用,可吹牛的內(nèi)容都是我干的堕绩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼邑时,長吁一口氣:“原來是場噩夢啊……” “哼奴紧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晶丘,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤黍氮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后浅浮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沫浆,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年滚秩,在試婚紗的時候發(fā)現(xiàn)自己被綠了专执。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡叔遂,死狀恐怖他炊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情已艰,我是刑警寧澤痊末,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站哩掺,受9級特大地震影響凿叠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一盒件、第九天 我趴在偏房一處隱蔽的房頂上張望蹬碧。 院中可真熱鬧,春花似錦炒刁、人聲如沸恩沽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罗心。三九已至,卻和暖如春城瞎,著一層夾襖步出監(jiān)牢的瞬間渤闷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工脖镀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留飒箭,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓蜒灰,卻偏偏與公主長得像弦蹂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子卷员,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容