我也正在找這款游戲,同問(wèn),以前在諾基亞上玩過(guò)。不過(guò)最早玩的一個(gè)版本是以隋唐為背景的,主角是唐太宗李世民,步兵弓手術(shù)士(水兵)騎兵槍兵(重步兵)最強(qiáng)是投石機(jī),不同地形對(duì)不同兵種有加成,祭司召喚尸體完全相同,有劇情,最終BOSS也是遠(yuǎn)程打擊。還有自由對(duì)戰(zhàn)模式超好玩的,簡(jiǎn)直就是策略戰(zhàn)棋啟蒙,可是這么多年過(guò)去已經(jīng)找不到了。
創(chuàng)新互聯(lián) - 成都服務(wù)器托管,四川服務(wù)器租用,成都服務(wù)器租用,四川網(wǎng)通托管,綿陽(yáng)服務(wù)器托管,德陽(yáng)服務(wù)器托管,遂寧服務(wù)器托管,綿陽(yáng)服務(wù)器托管,四川云主機(jī),成都云主機(jī),西南云主機(jī),成都服務(wù)器托管,西南服務(wù)器托管,四川/成都大帶寬,機(jī)柜大帶寬,四川老牌IDC服務(wù)商
Copyright ? 1999-2020, CSDN.NET, All Rights Reserved
java
打開(kāi)APP
成勝文
關(guān)注
設(shè)計(jì)模式 — 6大設(shè)計(jì)原則(單一職責(zé)和里氏替換原則) 原創(chuàng)
2022-11-04 01:26:20
成勝文
碼齡4年
關(guān)注
6大設(shè)計(jì)原則
說(shuō)明
單一職責(zé)原則
示例 一
示例 二
總結(jié)
里氏替換原則
示例 一
示例 二
示例 三
總結(jié)
說(shuō)明
6大設(shè)計(jì)原則來(lái)自于觀看 “設(shè)計(jì)模式之禪” 一書(shū)后的總結(jié)
單一職責(zé)原則
單一職責(zé)原則的英文名稱是Single Responsibility Principle,簡(jiǎn)稱是SRP。這個(gè)原則存在爭(zhēng)議之處在哪里呢?就是對(duì)職責(zé)的定義,什么是類的職責(zé),以及怎么劃分類的職責(zé)。下面舉個(gè)例子。
示例 一
正例:權(quán)限認(rèn)證(RBAC模式)(Role - Based Access Control,基于角色的訪問(wèn)控制,通過(guò)分
配和取消角色來(lái)完成用戶權(quán)限的授予和取消,使動(dòng)作主體(用戶)與資源的行為(權(quán)限)分離)。
1
2
1
2
再舉一個(gè)反例,用戶管理、修改用戶的信息、增加機(jī)構(gòu)(一個(gè)人屬于多個(gè)機(jī)構(gòu))、增加角色等,用戶有這么多的信息和行為要維護(hù),當(dāng)我們把這些寫(xiě)到一個(gè)接口中,都是用戶管理類嘛,如下示類圖:
用戶信息維護(hù)類圖
這個(gè)接口設(shè)計(jì)的問(wèn)題在于用戶的屬性和用戶的行為沒(méi)有分開(kāi),對(duì)于后期的維護(hù)特別不友好,應(yīng)該把用戶的信息
抽取成一個(gè)Bo(Bussiness Object,業(yè)務(wù)對(duì)象),把行為抽取成一個(gè)Biz(Business Logic,業(yè)務(wù)邏輯)
1
2
1
2
如下圖所示:
在這里插入圖片描述
代碼清單 1 - 1 分清職責(zé)后的代碼示例
.....
IUserBiz userInfo = new UserInfo();
//我要賦值了,我就認(rèn)為它是一個(gè)純粹的BO
IUserBO userBO = (IUserBO)userInfo;
userBO.setPassword("abc");
//我要執(zhí)行動(dòng)作了,我就認(rèn)為是一個(gè)業(yè)務(wù)邏輯類
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.deleteUser();
.....
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
動(dòng)作分析:為什么要把一個(gè)接口拆分成兩個(gè)呢?其實(shí),在實(shí)際的使用中,我們更傾向于使用兩個(gè)不同的類或接口:一個(gè)是IUserBO,一個(gè)是IUserBiz,類圖如下圖所示。
在這里插入圖片描述
歸納:?jiǎn)我宦氊?zé)原則的定義是:應(yīng)該有且僅有一個(gè)原因引起類的變更
示例 二
電話這玩意,是現(xiàn)代人都離不開(kāi),電話通話的時(shí)候有4個(gè)過(guò)程發(fā)生:撥號(hào)、通話、回應(yīng)、掛機(jī),那我們寫(xiě)一個(gè)接口,其類圖下所示:
在這里插入圖片描述
代碼清單:電話過(guò)程
public interface Iphone {
//撥通電話
public void dial(String phoneNumber);
//通話
public void chat(Object o);
//通話完畢,掛電話
public void hangup();
}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
這個(gè)接口乍看上去沒(méi)啥毛病,平常開(kāi)發(fā)也是這樣寫(xiě),單一職責(zé)原則要求一個(gè)接口或類只有一個(gè)原因引起變化,也就是一個(gè)接口或類只有一個(gè)職責(zé),它就負(fù)責(zé)一件事情,但是看上面的接口只負(fù)責(zé)一件事情嗎?是只有一個(gè)原因引起變化嗎?好像不是!
IPhone這個(gè)接口可不是只有一個(gè)職責(zé),它包含了兩個(gè)職責(zé):一個(gè)是協(xié)議管理,一個(gè)是數(shù)據(jù)傳送。dial()和hangup()兩個(gè)方法實(shí)現(xiàn)的是協(xié)議管理,分別負(fù)責(zé)撥號(hào)接通和掛機(jī);chat()實(shí)現(xiàn)的是數(shù)據(jù)的傳送,把我們說(shuō)的話轉(zhuǎn)換成模擬信息或數(shù)字信號(hào)傳遞到對(duì)方,然后再把對(duì)方傳遞過(guò)來(lái)的信號(hào)還原成我們聽(tīng)得懂的語(yǔ)言。我們可以這樣考慮這個(gè)問(wèn)題,協(xié)議接通的變化會(huì)引起這個(gè)接口或?qū)崿F(xiàn)類的變化嗎?會(huì)的!那數(shù)據(jù)傳送(想想看,電話不僅僅可以通話,還可以上網(wǎng))的變化會(huì)引起這個(gè)接口或?qū)崿F(xiàn)類的變化嗎?會(huì)的!那就很簡(jiǎn)單了,這里有兩個(gè)原因都引起了類的變化。這兩個(gè)職責(zé)會(huì)互相影響嗎?電話撥號(hào),我只要能接通就成,不管是電信的還是網(wǎng)通的協(xié)議;電話連接后還關(guān)心傳遞的是什么數(shù)據(jù)嗎?通過(guò)這樣的分析,我們發(fā)現(xiàn)類圖上的IPhone接口還包含了兩個(gè)職責(zé),而且這兩個(gè)職責(zé)的變化不互相影響,那就考慮拆分成兩個(gè)接口,如下圖所示:
在這里插入圖片描述
這個(gè)類圖看上去有點(diǎn)復(fù)雜了,完美滿足了單一職責(zé)原則的要求,每個(gè)接口職責(zé)分明,結(jié)構(gòu)清晰,但是一般在設(shè)計(jì)的時(shí)候不會(huì)采用這種方式,一個(gè)手機(jī)類要把ConnectionManager和DataTransfer組合在一塊才能使用。組合是一種強(qiáng)耦合關(guān)系,你和我都有共同的生命期,這樣的強(qiáng)耦合關(guān)系還不如使用接口實(shí)現(xiàn)的方式呢,而且還增加了類的復(fù)雜性,多了兩個(gè)類,經(jīng)過(guò)這樣的思考后,我們?cè)傩薷囊幌骂悎D,如下圖所示:
在這里插入圖片描述
這樣的設(shè)計(jì)才是完美的,一個(gè)類實(shí)現(xiàn)了兩個(gè)接口,把兩個(gè)職責(zé)融合在一個(gè)類中,你會(huì)覺(jué)得這個(gè)Phone有兩個(gè)原因引起變化了呀,是的,但是別忘記了我們是面向接口編程,我們對(duì)外公布的是接口而不是實(shí)現(xiàn)類。而且,如果真要實(shí)現(xiàn)類的單一職責(zé),這個(gè)就必須使用上面的組合模式了,這會(huì)引起類間耦合過(guò)重、類的數(shù)量增加等問(wèn)題,人為地增加了設(shè)計(jì)的復(fù)雜性。
總結(jié)
通過(guò)上面的例子,總結(jié)一下單一職責(zé)原則有什么好處:
1、類的復(fù)雜性降低,實(shí)現(xiàn)什么職責(zé)都有清晰明確的定義;
2、可讀性提高,復(fù)雜性降低,那當(dāng)然可讀性提高了;
3、可維護(hù)性提高,可讀性提高,那當(dāng)然更容易維護(hù)了;
4、變更引起的風(fēng)險(xiǎn)降低,變更是必不可少的,如果接口的單一職責(zé)做得好,一個(gè)接口修改只對(duì)相應(yīng)的實(shí)現(xiàn)類
有影響,對(duì)其他的接口無(wú)影響,這對(duì)系統(tǒng)的擴(kuò)展性、維護(hù)性都有非常大的幫助。
1
2
3
4
5
1
2
3
4
5
注意:?jiǎn)我宦氊?zé)原則提出了一個(gè)編寫(xiě)程序的標(biāo)準(zhǔn),用 “職責(zé)” 或 “變化原因” 來(lái)衡量接口或類設(shè)計(jì)得是否優(yōu)良,但是 “職責(zé)” 和 “變化原因” 都是不可度量的,因項(xiàng)目而異,因環(huán)境而異。
里氏替換原則
在面向?qū)ο蟮恼Z(yǔ)言中,繼承是必不可少的、非常優(yōu)秀的語(yǔ)言機(jī)制,它有如下優(yōu)點(diǎn):
1、代碼共享,減少創(chuàng)建類的工作量,每個(gè)子類都擁有父類的方法和屬性;
2、提高代碼的重用性;
3、子類可以形似父類,但又異于父類,“龍生龍,鳳生鳳,老鼠生來(lái)會(huì)打洞” 是說(shuō)子擁有父的 “種”,“世界上
沒(méi)有兩片完全相同的葉子” 是指明子與父的不同;
4、提高代碼的可擴(kuò)展性,實(shí)現(xiàn)父類的方法就可以 “為所欲為” 了,君不見(jiàn)很多開(kāi)源框架的擴(kuò)展接口都是通過(guò)
繼承父類來(lái)完成的;
5、提高產(chǎn)品或項(xiàng)目的開(kāi)放性
1
2
3
4
5
6
7
1
2
3
4
5
6
7
自然界的所有事物都是優(yōu)點(diǎn)和缺點(diǎn)并存的,即使是雞蛋,有時(shí)候也能挑出骨頭來(lái),繼承的缺點(diǎn)如下:
1、繼承是侵入性的,只要繼承,就必須擁有父類的所有屬性和方法;
2、降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束;
3、增加了耦合性。當(dāng)父類的常量、變量和方法被修改時(shí),必需要考慮子類的修改,而且在缺乏規(guī)范的環(huán)境下,
這種修改可能帶來(lái)非常糟糕的結(jié)果一大片的代碼需要重構(gòu)。
1
2
3
4
1
2
3
4
Java使用extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承,它采用了單一繼承的規(guī)則,C++則采用了多重繼承的規(guī)則,一個(gè)子類可以繼承多個(gè)父類。從整體上來(lái)看,利大于弊,怎么才能讓 “利” 的因素發(fā)揮最大的作用,同時(shí)減少 “弊” 帶來(lái)的麻煩呢? 解決方案是引入里氏替換原則 (Liskov Substitution Principle,LSP),什么是里氏替換原則呢?它有兩種定義:
第一種定義,也是最正宗的定義:如果對(duì)每一個(gè)類型為S的對(duì)象o1,都有類型為T(mén)的對(duì)象o2,使得以T定義的所有
程序P在所有的對(duì)象o1都代換成o2時(shí),程序P的行為沒(méi)有發(fā)生變化,那么類型S是類型T的子類型。
第二種定義:所有引用基類的地方必須能透明地使用其子類的對(duì)象。
1
2
3
4
1
2
3
4
第二個(gè)定義是最清晰明確的,通俗點(diǎn)講,只要父類能出現(xiàn)的地方子類就可以出現(xiàn),而且替換為子類也不會(huì)產(chǎn)生任何錯(cuò)誤或異常,使用者可能根本就不需要知道是父類還是子類。但是,反過(guò)來(lái)就不行了,有子類出現(xiàn)的地方,父類未必就能適應(yīng)。
示例 一
里氏替換原則為良好的繼承定義了一個(gè)規(guī)范,一句簡(jiǎn)單的定義包含了4層含義。
1.子類必須完全實(shí)現(xiàn)父類的方法,舉個(gè)例子,大家都打過(guò)CS吧,非常經(jīng)典的FPS類游戲,我們來(lái)描述一下里面用到的槍,如下圖:
在這里插入圖片描述
槍的主要職責(zé)是射擊,如何射擊在各個(gè)具體的子類中定義,手槍是單發(fā)射程比較近,步槍威力大射程遠(yuǎn),機(jī)槍用于掃射。在士兵類中定義了一個(gè)方法KillEnemy,使用槍來(lái)殺敵人,具體使用什么槍來(lái)殺敵人,調(diào)用的時(shí)候才知道,AbstractGun類的源程序
槍支的抽象類:
public abstract class AbstractGun {
//槍用來(lái)干什么?殺敵!
public abstract void shoot();
}
1
2
3
4
5
1
2
3
4
5
手槍、步槍、機(jī)槍的實(shí)現(xiàn)類如下面代碼
public class Handgun extends AbstractGun{
//手槍的特點(diǎn)是攜帶方便,射程短
@Override
public void shoot() {
System.out.println("手槍射擊...");
}
}
public class MachineGun extends AbstractGun{
@Override
public void shoot() {
System.out.println("機(jī)槍射擊...");
}
}
public class Rifle extends AbstractGun{
//步槍的特點(diǎn)是射程遠(yuǎn),威力大
@Override
public void shoot() {
System.out.println("步槍射擊...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
有了槍支,還要有能夠使用這些槍支的士兵,代碼清單如下:
士兵的實(shí)現(xiàn)類:
public class Soldier {
//定義士兵的槍支
private AbstractGun gun;
//給士兵一支槍
public void setGun(AbstractGun _gun) {
this.gun = _gun;
}
public void killEnemy() {
System.out.println("士兵開(kāi)始?xì)橙?..");
gun.shoot();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
定義士兵使用槍來(lái)殺敵,但是這把槍是抽象的,具體是手槍還是步槍需要在戰(zhàn)場(chǎng)前(也就是場(chǎng)景中)前通過(guò)setGun方法確定。場(chǎng)景類Client的代碼如下所示:
public class Client {
public static void main(String[] args) {
//產(chǎn)生三毛這個(gè)士兵
Soldier sanMao = new Soldier();
//給三毛一支槍
sanMao.setG
標(biāo)題名稱:關(guān)于士兵殺敵java代碼的信息
鏈接URL:http://muchs.cn/article36/doccepg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)、Google、網(wǎng)站改版、用戶體驗(yàn)、定制開(kāi)發(fā)、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容