設(shè)計模式入門

作者已經(jīng)搬遷去隔壁網(wǎng)站图张,也歡迎大家關(guān)注我們的寫作團隊:天星技術(shù)團隊垫挨。

前言

不知道是否有許多萌新跟我一樣韩肝,在看java源碼的時候,腦袋容易暈九榔。通常查一個方法哀峻,要跳幾個類出來,有些類動不動就上千行哲泊。像我這樣血氣方剛的少年剩蟀,哪靜得下心來理解這么多結(jié)構(gòu)復雜的代碼!還不如看點番劇切威,喝點快樂肥宅水育特!



看吧,不知道該如何下手去啃源碼先朦。不看吧缰冤,心里也急得很≡海總不能一直這樣放著吧棉浸,既然選擇了做這行, 還是得好好學習刺彩,畢竟面向工資編程迷郑。于是在某位大神的指導下,推薦我先學習設(shè)計模式迂苛。okk三热!那接下來我們就一起來學習設(shè)計模式鼓择!

什么是設(shè)計模式

  1. 設(shè)計模式(Design pattern)代表了最佳的實踐三幻,通常被有經(jīng)驗的面向?qū)ο蟮能浖_發(fā)人員所采用。
  2. 設(shè)計模式是軟件開發(fā)人員在軟件開發(fā)過程中面臨的一般問題的解決方案呐能。這些解決方案是眾多軟件開發(fā)人員經(jīng)過相當長的一段時間的試驗和錯誤總結(jié)出來的念搬。
  3. 設(shè)計模式是一套被反復使用的、多數(shù)人知曉的摆出、經(jīng)過分類編目的朗徊、代碼設(shè)計經(jīng)驗的總結(jié)

為何要學習設(shè)計模式

  1. 別人都學你不學,是想當咸魚嗎偎漫?大佬都懂你不懂爷恳,不想混進大佬圈裝逼了?
  2. 當你用模式描述的時候象踊,其他開發(fā)人員很容易知道你對設(shè)計的想法温亲。
  3. 使用模式談?wù)撥浖到y(tǒng)棚壁,可以讓你保持在設(shè)計層次上,而不會壓低到對象與類這種瑣碎的事情上栈虚。
  4. 當用模式名稱交流時袖外,你們之間交流的不只是模式名稱,而是一整套模式背后所象征的質(zhì)量魂务,特性曼验,約束。

設(shè)計原則

提倡使用設(shè)計模式的根本原因是為了代碼復用粘姜,增加可維護性△拚眨現(xiàn)在被命名的23種設(shè)計模式就是遵守了以下六大設(shè)計原則,才達到了代碼復用孤紧,增加可維護性的目的颖杏。

  • 單一職責原則
    There should never be more than one reason for a class to change.
    不要存在多于一個導致類變更的原因,一個類只承擔一個職責
  • 開閉原則
    Software entities like classes,modules and functions should be open for extension but closed for modifications.
    類坛芽、模塊留储、函數(shù),對擴展是開放的咙轩,對修改是封閉的获讳。
  • 里式替換原則
    Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
    子類可以擴展父類的功能,但不能改變父類原有的功能
  • 迪米特原則(最少知識原則)
    Only talk to you immediate friends.
    盡量減少對象之間的交互活喊,從而減小類之間的耦合丐膝。
  • 接口隔離原則
    The dependency of one class to another one should depend on the smallest possible interface.
    不要對外暴露沒有實際意義的接口。
  • 依賴倒置原則
    High level modules should not depends upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.
    核心思想:面向接口編程

走進設(shè)計原則

看了這么多理論東西了钾菊,再不來點代碼刺激刺激神經(jīng)帅矗,我都要點右上角了。接下來煞烫,我們就通過一個例子浑此,小寫一點代碼,讓我們更好的理解設(shè)計模式滞详!
現(xiàn)在我們來設(shè)計一個游戲人物凛俱。(為了突出重點,更好的理解料饥,只講部分功能蒲犬,理解要表達的意思即可,并不會真正的把所有功能實現(xiàn)岸啡。)

class Character {

    //使用武器
    fun useWeapon() {
           Log.i("TAG", "fist")
    }
}

現(xiàn)在有了一個初步人物模型的設(shè)計原叮,實現(xiàn)了攻擊的方法。當人物到了一定等級,可以轉(zhuǎn)職為魔法師奋隶,道士沛慢,戰(zhàn)士的話,我們可以這樣寫:

class Character {

    //使用武器
    fun useWeapon(profession : String) {
        if(profession.equals("magician")){
            Log.i("TAG", "火墻風咆哮")
        }
        if(profession.equals("taoist")){
            Log.i("TAG", "召喚4級寶寶")
        }
        if(profession.equals("warrior")){
            Log.i("TAG", "刀刀烈火")
        }
    }
}

如果每個方法都這樣寫达布,當方法數(shù)量增多的時候团甲,這樣的寫法就變得很雜亂無章。還導致了影響類變化原因不止一個黍聂,也就違反了“單一職責原則”躺苦。那我們現(xiàn)在換一種方式來寫:創(chuàng)建Magician,Taoist产还,Warrior三個類匹厘。在父類中采用重載的方式來實現(xiàn)useWeapon()。

    //魔法師使用武器
    fun useWeapon(magician: Magician) {
        Log.i("TAG", "火墻風咆哮")
    }
    //道士使用武器
    fun useWeapon(taoist: Taoist) {
        Log.i("TAG", "召喚4級寶寶")
    }
    //戰(zhàn)士使用武器
    fun useWeapon(warrior: Warrior) {
        Log.i("TAG", "刀刀烈火")
    }

這樣做更傻脐区,不僅沒有遵守單一職責原則愈诚,還違反了迪米特法則
實際上牛隅,當我們一看到這種需求炕柔,有點經(jīng)驗的都知道,應(yīng)該寫三個類媒佣,分別對應(yīng)魔法師匕累,道士,戰(zhàn)士默伍,而不是把所有東西寫進一個類里面來欢嘿。useWeapon方法寫在父類中的缺點已經(jīng)暴露出來了,直覺告訴我們也糊,這個方法是應(yīng)該寫在子類中的炼蹦。那父類中的這個方法留不留呢?里式替換原則告訴我們狸剃,子類可以擴展父類的功能掐隐,但不能改變父類原有的功能。于是乎……

class Magician : Character(){

    fun useWeapon(){
        Log.i("TAG", "火墻風咆哮")
    }
}
class Taoist : Character(){

    fun useWeapon(){
        Log.i("TAG", "召喚4級寶寶")
    }
}
class Warrior : Character() {

    fun useWeapon(){
        Log.i("TAG", "刀刀烈火")
    }
}

現(xiàn)在來看捕捂,好像沒什么問題哦瑟枫。但是卻沒有遵守依賴倒置原則斗搞。我們應(yīng)該盡量的針對接口編程指攒,而不是針對實現(xiàn)編程。而現(xiàn)在我們把實際的行為都寫在子類當中僻焚!這樣的壞處的是你必須去在每一個子類中手寫useWeapon方法允悦,修改起來相當麻煩!而且在代碼執(zhí)行時沒辦法更改具體行為(除非寫更多代碼,但那樣并劃不來)隙弛。

現(xiàn)在這樣寫還有點怪怪的架馋,明明每一個類有useWeapon(),卻不能寫進父類中全闷。很氣有沒有叉寂!
那我們把character寫成一個接口?把方法寫進去总珠?
牛逼屏鳍!真是太聰明了!既不違反單一職責原則局服,也不違反迪米特法則钓瞭,還遵守了里式替換原則。


牛逼個雞兒淫奔!

現(xiàn)在我們只考慮了玩家的角色山涡,游戲里的NPC怎么辦? 你打得過NPC唆迁?
假如我們已經(jīng)把父類寫成了接口鸭丛,再創(chuàng)建一個npc類,看看吧唐责。

interface Character {
    
    //使用武器
    fun useWeapon()
}
class NPC :Character {
    override fun useWeapon() {
        //空方法
    }
}

這樣造成了npc類里面有一個空方法系吩。你可能永遠都不會去用它。那放這兒有什么意思妒蔚?這樣寫還違反了接口隔離原則:不要對外暴露沒有實際意義的接口穿挨!不要對外暴露沒有實際意義的接口!不要對外暴露沒有實際意義的接口肴盏!
問題不大科盛!只需要打一個響指!我們重新理一理思緒菜皂!

現(xiàn)在問題在于我們要讓某些子類實現(xiàn)useWeapon()贞绵,而不是全部子類都要去實現(xiàn)useWeapon()。
okk的恍飘!useWeapon()既然不能放進接口里面榨崩,也不能放進父類里面,那我們就把這個方法單獨提出來章母,新寫一個useWeapon接口母蛛!

interface IUseWeapon {
    fun useWeapon()
}

然后讓有攻擊功能的子類來實現(xiàn)IUseWeapon接口。
emmmmmmmmm……
這樣使用接口還是得一個個去寫子類實現(xiàn)的具體方法乳怎,而且也沒有遵守依賴倒置原則彩郊,在上面我已經(jīng)寫過了,依賴倒置原則的核心思想就在于面向接口編程,那面向接口編程是個啥意思呢秫逝?

“針對接口編程”真正的意思是“針對超類型編程”恕出。

  • “針對接口編程”,關(guān)鍵就在于多態(tài)违帆!利用多態(tài)浙巫,程序可以針對超類型編程,執(zhí)行時會根據(jù)實際狀況執(zhí)行到真正的行為刷后,不會被綁死咋超類型的行為上狈醉。
  • “針對超類型編程”這句話才沧,可以更明確地說成變量的聲明類型應(yīng)該是超類型淮椰,通常是一個抽象類或者是一個接口鲤孵。
  • 只要是具體實現(xiàn)此超類型的類拍摇,所產(chǎn)生的對象漆弄,都可以指定給這個變量蔓罚。這也意味著眉踱,聲明類時不用理會以后會執(zhí)行時的真正對象裂逐!
//針對實現(xiàn)編程
var magician : Magician = Magician()

//針對接口\超類型編程
var character  : Character = Magician()

此時我們已經(jīng)有了一個IUseWeapon接口抱慌,里面只有一個useWeapon()方法逊桦。我們不能用子類直接實現(xiàn)IUseWeapon,也不能用父類直接實現(xiàn)IUseWeapon抑进,那我們就專門創(chuàng)建一個“行為類”來實現(xiàn)行為接口强经!

class UseFireWall : IUseWeapon {
    override fun useWeapon() {
        Log.i("TAG", "火墻風咆哮")
    }
}
class UseDogBaby : IUseWeapon{
    override fun useWeapon() {
        Log.i("TAG", "召喚4級寶寶")
    }
}
class UseFireKnife : IUseWeapon{
    override fun useWeapon() {
        Log.i("TAG", "刀刀烈火")
    }
}

這樣的設(shè)計,就讓使用武器這個行為跟character類無關(guān)了寺渗,還可以被其他對象服復用匿情。而新增一些使用武器行為時候,不會影響到既有的行為類信殊,也不會影響使用到行為類的character類炬称。

現(xiàn)在我們在整合一下整個人物的設(shè)計:
1. 擁有一個父類Character
2. 擁有四個子類,Magician, Taoist, Warrior, NPC
3. 有一個行為接口IUseWeapon
4. 有三個行為類實現(xiàn)了行為接口
目標:遵守六大設(shè)計原則的條件下涡拘,使Magician, Taoist, Warrior 才有useWeapon()

要實現(xiàn)攻擊的功能玲躯,那父類肯定得有調(diào)用useWeapon()的方法,也必須得擁有行為接口鳄乏。所以……

open class Character {
    lateinit var iUseWeapon :IUseWeapon
    
    fun coverUseWeapon(){
        iUseWeapon.useWeapon()
    }
}

在編譯時跷车,已經(jīng)能通過charater.coverUseWeapon()調(diào)用使用武器的方法。在代碼真正執(zhí)行時橱野,子類還沒對iUseWeapon進行聲明朽缴,所以在子類中要做的只是聲明iUseWeapon而已。這個時候仲吏,行為類實現(xiàn)行為接口的好處就體現(xiàn)出來了不铆,我們希望子類做什么樣的攻擊蝌焚,就可以聲明為什么樣的行為類裹唆,要想有新的新的攻擊動作誓斥,再創(chuàng)建一個行為類去實現(xiàn)IUseWeapon就可以了。反正實現(xiàn)的代碼沒有寫在子類中许帐,而是在行為類中劳坑,不用更改之前寫的所有代碼。

class Magician : Character(){
    init {
        iUseWeapon = UseFireWall()
    }
}

美滋滋成畦!就這么簡單的一行代碼距芬!但這樣還是不夠靈活,我們還是在子類中做了一小部分的具體實現(xiàn)(創(chuàng)建iUseWeapon的實例)循帐,也就是沒有完全的做到針對接口編程框仔,所以我們需要有一個可以更改iUseWeapon實例的方法。

java代碼中拄养,我們可以在父類中加入set方法离斩。

public void setIUseWeapon(iUseWeapon : IUseWeapon) {
        this.iUseWeapon = iUseWeapon ;
    }

kotlin代碼中,直接在聲明character對象處

//讓魔法師召喚4級寶寶
var character  : Character = Magician()
character.iUseWeapon  = UseDogBaby()

子類里面的init方法瘪匿,就可以完全不寫了跛梗。

最后

相信各位看到這里對六大原則其中五個都有了一定的理解,剩下一個開閉原則沒有提到棋弥,是因為核偿,這玩意兒不好講!
設(shè)計模式就是個經(jīng)驗性的東西顽染,你完全可以不照著這樣去寫你的代碼漾岳,只要遵守六大設(shè)計原則,都是好的設(shè)計粉寞。

以下是我“設(shè)計模式系列”文章蝗羊,歡迎大家關(guān)注留言投幣丟香蕉。
也可以進群跟大神們討論仁锯。qq群:557247785

設(shè)計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴展
由淺到深了解工廠模式
為了學習Rxjava耀找,年輕小伙竟作出這種事!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末业崖,一起剝皮案震驚了整個濱河市野芒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌双炕,老刑警劉巖狞悲,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異妇斤,居然都是意外死亡摇锋,警方通過查閱死者的電腦和手機丹拯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荸恕,“玉大人乖酬,你說我怎么就攤上這事∪谇螅” “怎么了咬像?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長生宛。 經(jīng)常有香客問我县昂,道長,這世上最難降的妖魔是什么陷舅? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任倒彰,我火速辦了婚禮,結(jié)果婚禮上莱睁,老公的妹妹穿的比我還像新娘待讳。我一直安慰自己,他們只是感情好缩赛,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布耙箍。 她就那樣靜靜地躺著,像睡著了一般酥馍。 火紅的嫁衣襯著肌膚如雪辩昆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天旨袒,我揣著相機與錄音汁针,去河邊找鬼。 笑死砚尽,一個胖子當著我的面吹牛施无,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播必孤,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猾骡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了敷搪?” 一聲冷哼從身側(cè)響起兴想,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赡勘,沒想到半個月后嫂便,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡闸与,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年毙替,在試婚紗的時候發(fā)現(xiàn)自己被綠了岸售。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡厂画,死狀恐怖凸丸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情木羹,我是刑警寧澤甲雅,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布解孙,位于F島的核電站坑填,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏弛姜。R本人自食惡果不足惜脐瑰,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望廷臼。 院中可真熱鬧苍在,春花似錦、人聲如沸荠商。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽莱没。三九已至初肉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饰躲,已是汗流浹背牙咏。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘹裂,地道東北人妄壶。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像寄狼,于是被迫代替她去往敵國和親丁寄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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