本文小編為大家詳細(xì)介紹“Java設(shè)計(jì)模式七大原則是什么及怎么實(shí)現(xiàn)”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Java設(shè)計(jì)模式七大原則是什么及怎么實(shí)現(xiàn)”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。
為九江等地區(qū)用戶(hù)提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及九江網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都做網(wǎng)站、成都網(wǎng)站建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、九江網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專(zhuān)業(yè)、用心的態(tài)度為用戶(hù)提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶(hù)的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
編寫(xiě)軟件過(guò)程中,程序員面臨著來(lái)自耦合性,內(nèi)聚性以及可維護(hù)性,可擴(kuò)展性,重用性,靈活性等多方面的挑戰(zhàn),設(shè)計(jì)模式是為了讓程序(軟件)。具有更好
1)代碼重用性(即:相同功能的代碼,不用多次編寫(xiě))
2)可讀性(即:編程規(guī)范性,便于其他程序員的閱讀和理解)
3)可擴(kuò)展性(即:當(dāng)需要增加新的功能時(shí),非常的方便,稱(chēng)為可維護(hù))
4)可靠性(即:當(dāng)我們?cè)黾有碌墓δ芎?,?duì)原來(lái)的功能沒(méi)有影響)
5)使程序呈現(xiàn)高內(nèi)聚,低耦合的特性
6)設(shè)計(jì)模式包含了面向?qū)ο蟮木瑁岸嗽O(shè)計(jì)模式,你就懂了面向?qū)ο蠓治龊驮O(shè)計(jì)(OOA/D)的精要“
7)Scott Mayers 在其巨著《Effective C++》就曾經(jīng)說(shuō)過(guò) :C++老手和C++新手的區(qū)別就是前者手背上有很多傷疤
設(shè)計(jì)模式原則,其實(shí)就是程序員在編程時(shí),應(yīng)當(dāng)遵守的原則,也是各種設(shè)計(jì)模式的基礎(chǔ)(即:設(shè)計(jì)模式為什么這樣設(shè)計(jì)的依據(jù))
設(shè)計(jì)模式常用的七大原則有 :
1)單一職責(zé)原則
2)接口隔離原則
3)依賴(lài)倒轉(zhuǎn)(倒置)原則
4)里氏替換原則
5)開(kāi)閉原則
6)迪米特法則
7)合成復(fù)用原則
對(duì)類(lèi)來(lái)說(shuō)的,即一個(gè)類(lèi)應(yīng)該只負(fù)責(zé)一項(xiàng)職責(zé)。如類(lèi)A負(fù)責(zé)兩個(gè)不同職責(zé) :職責(zé)1,職責(zé)2。當(dāng)職責(zé)1需求變更而改變A時(shí),可能造成職責(zé)2執(zhí)行錯(cuò)誤,所以需要將類(lèi)A的粒度分解為A1,A2
以交通工具案例講解
package com.example.testdemo.mode.principle; public class SingleResponsibility1 { public static void main(String[] args) { Vehicle vehicle = new Vehicle(); vehicle.run("摩托車(chē)"); vehicle.run("汽車(chē)"); vehicle.run("飛機(jī)"); } } // 交通工具類(lèi) /** * 方式1 : * 1 。 在方式1的run方法中,違反了單一職責(zé)原則 * 2 。 解決的方案非常第二季簡(jiǎn)單,根據(jù)交通工具運(yùn)行方法不同,分解成不同類(lèi)即可 */ class Vehicle { public void run(String vehicle) { System.out.println(vehicle + " 在公路上運(yùn)行。。。。。"); } } package com.example.testdemo.mode.principle; public class SingleResponsibility2 { public static void main(String[] args) { ReadVehicle readVehicle = new ReadVehicle(); readVehicle.run("摩托車(chē)"); readVehicle.run("汽車(chē)"); AirVehicle airVehicle = new AirVehicle(); airVehicle.run("飛機(jī)"); WaterVehicle waterVehicle = new WaterVehicle(); waterVehicle.run("輪船"); } } /** * 方案2分析 : * 1 :遵守單一職責(zé)原則 * 2 :但是這樣改動(dòng)大,即將類(lèi)分解,同時(shí)修改客戶(hù)端 * 3 :改進(jìn) :直接修改Vehicle類(lèi),改動(dòng)的代碼會(huì)比較少 =》方案3 * */ class ReadVehicle { public void run(String vehicle) { System.out.println(vehicle + "公路運(yùn)行"); } } class AirVehicle { public void run(String vehicle) { System.out.println(vehicle + "天空運(yùn)行"); } } class WaterVehicle { public void run(String vehicle) { System.out.println(vehicle + "水中運(yùn)行"); } } package com.example.testdemo.mode.principle; public class SingleResponsibility3 { public static void main(String[] args) { Vehicle2 vehicle2 = new Vehicle2(); vehicle2.run("汽車(chē)"); vehicle2.runAir("飛機(jī)"); vehicle2.runWater("輪船"); } } /** * 方案3的分析 : * 1 :這種修改方法沒(méi)有對(duì)原來(lái)的類(lèi)做大的修改,只是增加方法 * 2 :這里雖然沒(méi)有在類(lèi)這個(gè)級(jí)別上遵守單一職責(zé)原則,但是在方法級(jí)別上,仍然是遵守單一職責(zé) */ class Vehicle2 { public void run(String vehicle) { System.out.println(vehicle + " 在公路上運(yùn)行。。。。。"); } public void runAir(String vehicle) { System.out.println(vehicle + " 在天空上運(yùn)行。。。。。"); } public void runWater(String vehicle) { System.out.println(vehicle + " 在水中運(yùn)行。。。。。"); } }
單一職責(zé)原則注意事項(xiàng)和細(xì)節(jié)
(1) 降低類(lèi)的復(fù)雜度,一個(gè)類(lèi)只負(fù)責(zé)一項(xiàng)職責(zé)。
(2)提高類(lèi)的可讀性,可維護(hù)性。
(3)降低變更引起的風(fēng)險(xiǎn)。
(4)通常情況下,我們應(yīng)當(dāng)遵守單一職責(zé)原則,只有邏輯足夠簡(jiǎn)單,才可以在代碼級(jí)違反單一職責(zé)原則 :只有類(lèi)中方法數(shù)量足夠少,可以在方法級(jí)別保存單一職責(zé)原則。
(1)客戶(hù)端不應(yīng)該依賴(lài)它不需要的接口,即一個(gè)類(lèi)對(duì)另一個(gè)類(lèi)的依賴(lài)應(yīng)該建立在最小的接口上
(2)先看一張圖
(3)類(lèi)A通過(guò)接口Interface1依賴(lài)類(lèi)B,類(lèi)C通過(guò)接口Interface1依賴(lài)類(lèi)D,如果接口Interface1對(duì)于類(lèi)A和類(lèi)C來(lái)說(shuō)不是最小接口,那么類(lèi)B和類(lèi)C必須去實(shí)現(xiàn)他們不需要的方法。
(4)按隔離原則應(yīng)當(dāng)這樣處理 :
將接口Interface1拆分為獨(dú)立的幾個(gè)接口(這里我們拆分3個(gè)接口),類(lèi)A和類(lèi)C分別與他們需要的接口建立依賴(lài)關(guān)系,也就是采用接口隔離原則
1)類(lèi)A通過(guò)接口Interface1依賴(lài)類(lèi)B,類(lèi)C通過(guò)接口Interface1依賴(lài)類(lèi)D。
2)沒(méi)有使用接口隔離原則的代碼
package com.example.testdemo.mode.principle.segregation; import io.swagger.models.auth.In; public class Segregation1 { public static void main(String[] args) { } } /** * 接口 */ interface Interface1 { void operation1(); void operation2(); void operation3(); void operation4(); void operation5(); } class B implements Interface1 { @Override public void operation1() { System.out.println(" B 實(shí)現(xiàn)了 operation1"); } @Override public void operation2() { System.out.println(" B 實(shí)現(xiàn)了 operation2"); } @Override public void operation3() { System.out.println(" B 實(shí)現(xiàn)了 operation3"); } @Override public void operation4() { System.out.println(" B 實(shí)現(xiàn)了 operation4"); } @Override public void operation5() { System.out.println(" B 實(shí)現(xiàn)了 operation5"); } } class D implements Interface1 { @Override public void operation1() { System.out.println(" D 實(shí)現(xiàn)了 operation1"); } @Override public void operation2() { System.out.println(" D 實(shí)現(xiàn)了 operation2"); } @Override public void operation3() { System.out.println(" D 實(shí)現(xiàn)了 operation3"); } @Override public void operation4() { System.out.println(" D 實(shí)現(xiàn)了 operation4"); } @Override public void operation5() { System.out.println(" D 實(shí)現(xiàn)了 operation5"); } } /** * A 類(lèi)通過(guò)接口Interface1 依賴(lài)(使用)B類(lèi),但是只會(huì)用到1,2,3方法 */ class A { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend2(Interface1 interface1) { interface1.operation2(); } public void depend3(Interface1 interface1) { interface1.operation3(); } } /** * C 類(lèi)通過(guò)接口Interface1 依賴(lài)(使用)D類(lèi),但是只會(huì)用到1,4,5方法 */ class C { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend4(Interface1 interface1) { interface1.operation4(); } public void depend5(Interface1 interface1) { interface1.operation5(); } }
應(yīng)傳統(tǒng)方法的問(wèn)題和接口隔離原則改進(jìn)
(1)類(lèi)A通過(guò)Interface1依賴(lài)類(lèi)B,類(lèi)C通過(guò)接口Interface1依賴(lài)類(lèi)D,如果接口Interface1對(duì)于類(lèi)A和類(lèi)C來(lái)說(shuō)不是最小接口,那么類(lèi)B和類(lèi)C必須去實(shí)現(xiàn)他們不需要的方法。
(2)將接口Interface1拆分為獨(dú)立的幾個(gè)接口,類(lèi)A和類(lèi)C分別與他們需要的接口建立依賴(lài)關(guān)系。也就是采用接口隔離原則。
(3)接口Interface1中出現(xiàn)的方法,根據(jù)實(shí)際情況拆分為三個(gè)接口
(4)代碼實(shí)現(xiàn)
package com.example.testdemo.mode.principle.segregation1; public class Segregation2 { public static void main(String[] args) { // 使用一把 A a = new A(); // A 類(lèi)通過(guò)接口去依賴(lài)B類(lèi) a.depend1(new B()); a.depend2(new B()); a.depend3(new B()); // C 類(lèi)通過(guò)接口去依賴(lài)(使用)D類(lèi) C c = new C(); c.depend1(new D()); c.depend4(new D()); c.depend5(new D()); } } /** * 接口 */ interface Interface1 { void operation1(); } interface Interface2 { void operation2(); void operation3(); } interface Interface3 { void operation4(); void operation5(); } class B implements Interface1, Interface2 { @Override public void operation1() { System.out.println(" B 實(shí)現(xiàn)了 operation1"); } @Override public void operation2() { System.out.println(" B 實(shí)現(xiàn)了 operation2"); } @Override public void operation3() { System.out.println(" B 實(shí)現(xiàn)了 operation3"); } } class D implements Interface1, Interface3 { @Override public void operation1() { System.out.println(" D 實(shí)現(xiàn)了 operation1"); } @Override public void operation4() { System.out.println(" D 實(shí)現(xiàn)了 operation4"); } @Override public void operation5() { System.out.println(" D 實(shí)現(xiàn)了 operation5"); } } /** * A 類(lèi)通過(guò)接口Interface1 ,Interface2 依賴(lài)(使用)B類(lèi),但是只會(huì)用到1,2,3方法 */ class A { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend2(Interface2 interface1) { interface1.operation2(); } public void depend3(Interface2 interface1) { interface1.operation3(); } } /** * C 類(lèi)通過(guò)接口Interface1 ,Interface3 依賴(lài)(使用)D類(lèi),但是只會(huì)用到1,4,5方法 */ class C { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend4(Interface3 interface1) { interface1.operation4(); } public void depend5(Interface3 interface1) { interface1.operation5(); } }
依賴(lài)倒轉(zhuǎn)原則(Dependence Inversion Principle)是指 :
(1)高層模塊不應(yīng)該依賴(lài)底層模塊,二者都應(yīng)該依賴(lài)其抽象
(2)抽象不應(yīng)該依賴(lài)細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴(lài)抽象
(3)依賴(lài)倒轉(zhuǎn)(倒置)的中心思想是面向接口編程
(4)依賴(lài)倒轉(zhuǎn)原則是基于這樣的設(shè)計(jì)理念 :相對(duì)于細(xì)節(jié)的多變性,抽象的東西要穩(wěn)定的多。以抽象為基礎(chǔ)搭建的架構(gòu)比以細(xì)節(jié)為基礎(chǔ)的架構(gòu)要穩(wěn)定的多。在java中,抽象指的是接口或抽象類(lèi),細(xì)節(jié)就是具體的實(shí)現(xiàn)類(lèi)。
(5)使用接口或抽象類(lèi)的目的是制定好規(guī)范,而不涉及任何具體的操作,把展示細(xì)節(jié)的任務(wù)交給他們的實(shí)現(xiàn)類(lèi)去完成。
1)方案1 + 分析說(shuō)明
package com.example.testdemo.mode.principle.inversion; public class DependecyInversion { public static void main(String[] args) { Person person = new Person(); person.receive(new Email()); } } class Email { public String getInfo() { return "電子郵件信息 :hello,world"; } } /** * 完成Person接收消息的功能 * 方式1分析 * 1。簡(jiǎn)單,比較容易想到 * 2。如果我們獲取的對(duì)象是微信,短信等等,則新增類(lèi),同時(shí)Persons也要增加相應(yīng)的接收方法 * 3。解決思路 :引入一個(gè)抽象的接口IReceiver,表示接收者,這樣Person類(lèi)與接口IReceiver發(fā)生依賴(lài) * 因?yàn)镋mail,微信等等屬于接收的范圍,他們各自實(shí)現(xiàn)IReceiver接口就ok,這樣我們就符合依賴(lài)倒轉(zhuǎn)原則 */ class Person { public void receive(Email email) { System.out.println(email.getInfo()); } }
2)方案2(依賴(lài)倒轉(zhuǎn))+ 分析說(shuō)明
package com.example.testdemo.mode.principle.inversion.inprove; public class DependecyInversion { public static void main(String[] args) { // 客戶(hù)端無(wú)需改變 Person person = new Person(); person.receive(new Email()); person.receive(new WeiXin()); } } /** * 定義接口 */ interface IReceiver { String getInfo(); } class Email implements IReceiver{ @Override public String getInfo() { return "電子郵件信息 :hello,world"; } } /** * 增加微信 */ class WeiXin implements IReceiver { @Override public String getInfo() { return "微信消息 :hello ok"; } } /** * 方式2 */ class Person { /** * 這里是我們對(duì)接口的依賴(lài) * @param iReceiver */ public void receive(IReceiver iReceiver) { System.out.println(iReceiver.getInfo()); } }
依賴(lài)關(guān)系傳遞的三種方式 :
1)接口傳遞
2)構(gòu)造方法傳遞
3)setter方法傳遞
package com.example.testdemo.mode.principle.inversion.inprove; public class Dependecy { public static void main(String[] args) { IOpenAndClose iOpenAndClose = new OpenAndClose(); iOpenAndClose.open(new ChangHong()); IOpenAndClose2 iOpenAndClose2 = new OpenAndClose2(new XiaoMi()); iOpenAndClose2.open(); IOpenAndClose3 iOpenAndClose3 = new OpenAndClose3(); iOpenAndClose3.setTv(new SanXing()); iOpenAndClose3.open(); } } /** * 方式1 :通過(guò)接口傳遞實(shí)現(xiàn)依賴(lài) */ interface IOpenAndClose { /** * 抽象方法,接收接口 * @param tv */ void open(ITV tv); } /** * ITV接口 */ interface ITV { void play(); } class ChangHong implements ITV { @Override public void play() { System.out.println("長(zhǎng)虹電視機(jī)打開(kāi)"); } } /** * 實(shí)現(xiàn)接口 */ class OpenAndClose implements IOpenAndClose { @Override public void open(ITV tv) { tv.play(); } } /** * 方式2 :通過(guò)構(gòu)造方法依賴(lài)傳遞 */ interface IOpenAndClose2 { /** * 抽象方法 */ void open(); } /** * ITV接口 */ interface ITV2 { void play(); } class XiaoMi implements ITV2 { @Override public void play() { System.out.println("小米電視機(jī)打開(kāi)"); } } class OpenAndClose2 implements IOpenAndClose2 { /** * 成員屬性 */ public ITV2 tv; /** * 構(gòu)造方法 * @param itv2 */ public OpenAndClose2(ITV2 itv2) { this.tv = itv2; } @Override public void open() { this.tv.play(); } } /** * 方式3,通過(guò)setter方法傳遞 */ interface IOpenAndClose3 { /** * 抽象方法 */ void open(); void setTv(ITV3 tv); } /** * ITV接口 */ interface ITV3 { void play(); } class SanXing implements ITV3 { @Override public void play() { System.out.println("三星電視打開(kāi)"); } } class OpenAndClose3 implements IOpenAndClose3 { private ITV3 itv3; @Override public void open() { this.itv3.play(); } @Override public void setTv(ITV3 tv) { this.itv3 = tv; } }
依賴(lài)倒轉(zhuǎn)原則的注意事項(xiàng)和細(xì)節(jié)
1)底層模塊盡力都要有抽象類(lèi)或接口,或者兩者都有,程序穩(wěn)定性更好。
2)變量的聲明類(lèi)型盡量是抽象類(lèi)或接口,這樣我們的變量引用和實(shí)際對(duì)象間,就存在一個(gè)緩存層,利于程序擴(kuò)展和優(yōu)化。
3)繼承時(shí)遵循里氏替換原則。
1)繼承包含這樣一層含義 :父類(lèi)中凡是已經(jīng)實(shí)現(xiàn)好的方法,實(shí)際上是在設(shè)定規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類(lèi)必須遵循這些契約,但是如果子類(lèi)對(duì)象這些已經(jīng)實(shí)現(xiàn)的方法任意修改,就會(huì)對(duì)整個(gè)繼承體系造成破壞。
2)繼承在給程序設(shè)計(jì)帶來(lái)便利的同時(shí),也帶來(lái)類(lèi)弊端。比如使用繼承會(huì)給程序帶來(lái)侵入性,程序的可移植性降低,增加對(duì)象間的耦合性,如果一個(gè)類(lèi)被其他的類(lèi)所繼承,則當(dāng)這個(gè)類(lèi)需要修改時(shí),必須考慮到所有的子類(lèi),并且父類(lèi)修改后,所有涉及到子類(lèi)的功能都有可能產(chǎn)生故障。
3)問(wèn)題提出 :在編程中,如何正確的使用繼承?=》里氏替換原則
1)里氏替換原則(Liskov Substitution Principle)在1988年,由麻省理工學(xué)院的一位姓里的女士提出的。
2)如果對(duì)每個(gè)類(lèi)型為T(mén)1的對(duì)象O1,都有類(lèi)型為T(mén)2的對(duì)象O2,使得以T1定義的所有程序P在所有的對(duì)象O1都代換成O2時(shí),程序P的行為沒(méi)有變化,那么類(lèi)型T2是類(lèi)型T1的子類(lèi)型。換句話說(shuō),所有引用基類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象。
3)在使用繼承時(shí),遵循里氏替換原則,在子類(lèi)中盡量不要重寫(xiě)父類(lèi)的方法。
4)里氏替換原則告訴我們,繼承實(shí)際上讓兩個(gè)類(lèi)耦合性增強(qiáng)了,在適當(dāng)?shù)那闆r下,可以通過(guò)聚合、組合、依賴(lài)來(lái)解決問(wèn)題。
該看個(gè)程序,思考下問(wèn)題和解決思路
package com.example.testdemo.mode.principle.liskov; public class Liskov { public static void main(String[] args) { A a = new A(); System.out.println("11 - 3 = " + a.funcl(11, 3)); System.out.println("1 - 8 = " + a.funcl(1, 8)); System.out.println("-----------------"); B b = new B(); // 這里本意是求出11 - 3 System.out.println("11 - 3 = " + b.funcl(11, 3)); // 1 - 8 System.out.println("1 - 8 = " + b.funcl(1, 8)); System.out.println("11 + 3 + 9 = " + b.func2(11, 3)); } } class A { /** * 返回兩個(gè)數(shù)的差 * * @param num1 * @param num2 * @return */ public int funcl(int num1, int num2) { return num1 - num2; } } /** * B類(lèi)繼承類(lèi)A * * 增加類(lèi)一個(gè)新功能 :完成兩個(gè)數(shù)相加,然后和9 求和 */ class B extends A { /** * 這里,重寫(xiě)類(lèi)A類(lèi)的方法,可能是無(wú)意識(shí) * @param a * @param b * @return */ @Override public int funcl(int a, int b) { return a + b; } public int func2(int a, int b) { return funcl(a, b) + 9; } }
1)我們發(fā)現(xiàn)原來(lái)運(yùn)行正常的相減功能發(fā)生類(lèi)錯(cuò)誤。原因就是類(lèi)B無(wú)意中重寫(xiě)父類(lèi)的方法,造成原有功能出現(xiàn)錯(cuò)誤。在實(shí)際編程中,我們常常會(huì)通過(guò)重寫(xiě)父類(lèi)的方法完成新的功能,這樣寫(xiě)起來(lái)雖然簡(jiǎn)單,但整個(gè)繼承體系的復(fù)用性會(huì)比較差。特別是運(yùn)行多態(tài)比較頻繁的時(shí)候。
2)通用的做法是 :原來(lái)的父類(lèi)和子類(lèi)都繼承一個(gè)更通俗的基類(lèi),原有的繼承關(guān)系去掉,采用依賴(lài)、聚合、組合等關(guān)系代替。
3)改進(jìn)方案。
package com.example.testdemo.mode.principle.improve; public class Liskov { public static void main(String[] args) { A a = new A(); System.out.println("11 - 3 = " + a.func1(11, 3)); System.out.println("1 - 8 = " + a.func1(1, 8)); System.out.println("--------------------------"); B b = new B(); // 因?yàn)锽類(lèi)不再繼承A類(lèi),因此調(diào)用者,不會(huì)再funcl是求減法 // 調(diào)用完成的功能就會(huì)很明確 // 這里本意是求出 11 + 3 System.out.println("11 + 3 = " + b.func1(11, 3)); // 1 + 8 System.out.println("1 + 8 = " + b.func1(1, 8)); System.out.println("11 + 3 + 9 = " + b.func2(11, 3)); // 使用組合仍然可以使用到A類(lèi)相關(guān)方法 // 這里本意是求出 11 - 3 System.out.println("11 - 3 = " + b.func3(11, 3)); } } /** * 創(chuàng)建一個(gè)更加基礎(chǔ)的基類(lèi) */ class Base { // 把更加基礎(chǔ)的方法和成員寫(xiě)B(tài)ase類(lèi) } /** * A 類(lèi) */ class A extends Base { /** * 返回兩個(gè)數(shù)的差 * @param num1 * @param num2 * @return */ public int func1(int num1, int num2) { return num1 - num2; } } /** * B類(lèi) 繼承了 A * * 增加類(lèi)一個(gè)新功能 :完成兩個(gè)數(shù)相加,然后和9 求和 */ class B extends Base { /** * 如果B需要使用A類(lèi)的方法,使用組合關(guān)系 */ private A a = new A(); /** * 這里,重寫(xiě)了A類(lèi)方法,可能是無(wú)意識(shí) * * @param a * @param b * @return */ public int func1(int a, int b) { return a + b; } public int func2(int a, int b) { return func1(a, b) + 9; } /** * 我們?nèi)匀幌胧褂肁的方法 * @param a * @param b * @return */ public int func3(int a, int b) { return this.a.func1(a,b); } }
1)開(kāi)閉原則(Open Closed Principle)是編程中最基礎(chǔ)、最重要的設(shè)計(jì)原則
2)一個(gè)軟件實(shí)體如類(lèi),模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放(對(duì)提供方),對(duì)修改關(guān)閉(對(duì)使用方)。用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。
3)當(dāng)軟件需要變化時(shí),盡量通過(guò)擴(kuò)展軟件實(shí)體的行為來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)變化。
4)編程中遵循其它原則,以及使用設(shè)計(jì)模式的目的就是遵循開(kāi)閉原則。
看一個(gè)畫(huà)圖形的功能。
類(lèi)圖設(shè)計(jì),如下 :
package com.example.demo.ocp; public class Ocp { public static void main(String[] args) { // 使用可靠存在的問(wèn)題 GraphicEditor graphicEditor = new GraphicEditor(); graphicEditor.drawShape(new Rectangle()); graphicEditor.drawShape(new Circle()); graphicEditor.drawShape(new Triangle()); } } /** * 這是一個(gè)用于繪圖的類(lèi)(使用方) */ class GraphicEditor { /** * 接收Shape對(duì)象,然后根據(jù)type,來(lái)繪制不同的圖形 * @param shape */ public void drawShape(Shape shape) { if (shape.m_type == 1) { drawRectangle(shape); } else if (shape.m_type == 2) { drawCircle(shape); } else if (shape.m_type == 3) { drawTriangle(shape); } } /** * 繪制三角形 * @param shape */ private void drawTriangle(Shape shape) { System.out.println("繪制三角形"); } /** * 繪制圓形 * @param shape */ private void drawCircle(Shape shape) { System.out.println("繪制圓形"); } /** * 繪制矩形 * @param shape */ private void drawRectangle(Shape shape) { System.out.println("繪制矩形"); } } /** * Shape類(lèi),基類(lèi) */ class Shape { int m_type; } class Rectangle extends Shape { Rectangle() { super.m_type = 1; } } class Circle extends Shape { Circle() { super.m_type = 2; } } /** * 新增畫(huà)三角形 */ class Triangle extends Shape { Triangle() { super.m_type = 3; } }
1)優(yōu)點(diǎn)是比較好理解,簡(jiǎn)單易操作。
2)缺點(diǎn)是違反了設(shè)計(jì)模式的ocp原則,即對(duì)擴(kuò)展開(kāi)放(提供方),對(duì)修改關(guān)閉(使用方)。即當(dāng)我們給類(lèi)增加新功能的時(shí)候,盡量不修改代碼,或者盡可能少修改代碼。
3)比如我們這時(shí)要新增加一個(gè)圖形種類(lèi) 三角形,我們需要做很多修改,修改的地方比較多。
思路 : 把創(chuàng)建Shape類(lèi)做成抽象類(lèi),并提供一個(gè)抽象的draw方法,讓子類(lèi)去實(shí)現(xiàn)即可,這樣我們有新的圖形種類(lèi)時(shí),只需要讓新的圖形類(lèi)繼承Shape,并實(shí)現(xiàn)draw方法即可,使用方的代碼就不需要修改 -》
滿足了開(kāi)閉原則
改進(jìn)后的代碼 :
package com.example.demo.ocp.improve; public class Ocp { public static void main(String[] args) { // 使用看看存在的問(wèn)題 GraphicEditor graphicEditor = new GraphicEditor(); graphicEditor.drawShape(new Rectangle()); graphicEditor.drawShape(new Circle()); graphicEditor.drawShape(new Triangle()); graphicEditor.drawShape(new OtherGraphic()); } } /** * 這是一個(gè)用于繪圖的類(lèi)(使用方) */ class GraphicEditor { /** * 接收Shape對(duì)象,調(diào)用draw方法 * @param shape */ public void drawShape(Shape shape) { shape.draw(); } } /** * Shape類(lèi),基類(lèi) */ abstract class Shape { int m_type; /** * 抽象方法 */ public abstract void draw(); } class Rectangle extends Shape { Rectangle() { super.m_type = 1; } @Override public void draw() { System.out.println("繪制矩形"); } } class Circle extends Shape { Circle() { super.m_type = 2; } @Override public void draw() { System.out.println("繪制圓形"); } } /** * 新增畫(huà)三角形 */ class Triangle extends Shape { Triangle() { super.m_type = 3; } @Override public void draw() { System.out.println("繪制三角形"); } } /** * 新增一個(gè)圖形 */ class OtherGraphic extends Shape { OtherGraphic() { super.m_type = 4; } @Override public void draw() { System.out.println("繪制其他圖形"); } }
1)一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最少的了解。
2)類(lèi)與類(lèi)關(guān)系越密切,耦合度越大。
3)迪米特法則(Demeter Principle)又叫最少知道原則,即一個(gè)類(lèi)對(duì)自己依賴(lài)的類(lèi)知道的越少越好。也就是說(shuō),對(duì)于被依賴(lài)的類(lèi)不管多么復(fù)雜,都盡量將邏輯封裝在類(lèi)的內(nèi)部。對(duì)外除了提供的public方法,不對(duì)外泄露任何信息。
4)迪米特法則還有個(gè)簡(jiǎn)單的定義 :只與直接的朋友通信。
5)直接的朋友 :每個(gè)對(duì)象都會(huì)與其他對(duì)象有耦合關(guān)系,只要兩個(gè)對(duì)象之間有耦合關(guān)系,我們就說(shuō)這兩個(gè)對(duì)象之間是朋友關(guān)系。耦合的方式很多,依賴(lài)、關(guān)聯(lián)組合、聚合等。其中,我們稱(chēng)出現(xiàn)成員變量,方法參數(shù),方法返回值中的類(lèi)為直接的朋友,而出現(xiàn)在局部變量中的類(lèi)不是直接的朋友。也就是說(shuō),陌生的類(lèi)最好不要以局部變量的形式出現(xiàn)在類(lèi)的內(nèi)部。
1)有一個(gè)學(xué)校,下屬有各個(gè)學(xué)院和總部,現(xiàn)要求打印出學(xué)??偛繂T工ID和學(xué)院?jiǎn)T工的id
2)編程實(shí)現(xiàn)上面的功能,看代碼演示
3)代碼演示
package com.example.demo.demeter; import java.util.ArrayList; import java.util.List; /** * 客戶(hù)端 */ public class Demeter1 { public static void main(String[] args) { // 創(chuàng)建一個(gè) SchoolManager 對(duì)象 SchoolManager schoolManager = new SchoolManager(); // 輸出學(xué)院的員工id 和 學(xué)院總部的員工信息 schoolManager.printAllEmployee(new CollegeManager()); } } /** * 學(xué)??偛繂T工類(lèi) */ class Employee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 學(xué)院的員工類(lèi) */ class CollegeEmployee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 管理學(xué)院?jiǎn)T工的管理類(lèi) */ class CollegeManager { /** * 返回學(xué)院的所有員工 * @return */ public List<CollegeEmployee> getAllEmployee() { List<CollegeEmployee> employees = new ArrayList<>(); // 這里我們?cè)黾恿?0個(gè)員工到list for (int i = 0; i < 10; i++) { CollegeEmployee collegeEmployee = new CollegeEmployee(); collegeEmployee.setId("學(xué)院?jiǎn)T工 id = " + i); employees.add(collegeEmployee); } return employees; } } /** * 學(xué)校管理類(lèi) * * 分析 SchoolManager 類(lèi)的直接朋友類(lèi)有哪些 Employee、CollegeManager * CollegeEmployee 不是 直接朋友,而是一個(gè)陌生類(lèi),這樣違背了迪米特法則 * */ class SchoolManager { /** * 返回學(xué)校總部的員工 * @return */ public List<Employee> getAllEmployee() { List<Employee> list = new ArrayList<>(); // 這里我們?cè)黾恿?個(gè)員工到list for (int i = 0; i < 5; i++) { Employee employee = new Employee(); employee.setId("學(xué)??偛繂T工 id = " + i); list.add(employee); } return list; } /** * 該方法完成輸出學(xué)??偛亢蛯W(xué)院?jiǎn)T工信息 (id) * @param collegeManager */ void printAllEmployee(CollegeManager collegeManager) { // 分析問(wèn)題 // 1. 這里的 CollegeEmployee 不是 SchoolManageer的直接朋友 // 2. CollegeEmployee 是以局部變量方式出現(xiàn)在 SchoolManager // 3. 違反了 迪米特法則 // 獲取到學(xué)院?jiǎn)T工 List<CollegeEmployee> allEmployee = collegeManager.getAllEmployee(); System.out.println("-------------學(xué)院?jiǎn)T工-------------"); for (CollegeEmployee collegeEmployee : allEmployee) { System.out.println(collegeEmployee.getId()); } // 獲取到學(xué)院總部員工 List<Employee> employee = this.getAllEmployee(); System.out.println("-----------學(xué)??偛繂T工-------------"); for (Employee employee1 : employee) { System.out.println(employee1.getId()); } } }
1)前面設(shè)計(jì)的問(wèn)題在于SchoolManager中,CollegeEmployee類(lèi)并不是SchoolManager類(lèi)的直接朋友(分析)
2)按照迪米特法則,應(yīng)該避免類(lèi)中出現(xiàn)這樣非直接朋友關(guān)系的耦合
3)對(duì)代碼按照迪米特法則進(jìn)行改進(jìn)。
4)代碼演示
package com.example.demo.demeter.improve; import java.util.ArrayList; import java.util.List; /** * 客戶(hù)端 */ public class Demeter1 { public static void main(String[] args) { // 創(chuàng)建一個(gè) SchoolManager 對(duì)象 SchoolManager schoolManager = new SchoolManager(); // 輸出學(xué)院的員工id 和 學(xué)院總部的員工信息 schoolManager.printAllEmployee(new CollegeManager()); } } /** * 學(xué)校總部員工類(lèi) */ class Employee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 學(xué)院的員工類(lèi) */ class CollegeEmployee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 管理學(xué)院?jiǎn)T工的管理類(lèi) */ class CollegeManager { /** * 返回學(xué)院的所有員工 * @return */ public List<CollegeEmployee> getAllEmployee() { List<CollegeEmployee> employees = new ArrayList<>(); // 這里我們?cè)黾恿?0個(gè)員工到list for (int i = 0; i < 10; i++) { CollegeEmployee collegeEmployee = new CollegeEmployee(); collegeEmployee.setId("學(xué)院?jiǎn)T工 id = " + i); employees.add(collegeEmployee); } return employees; } /** * 輸出學(xué)院?jiǎn)T工的信息 */ public void printEmployee() { // 獲取到學(xué)院?jiǎn)T工 List<CollegeEmployee> allEmployee = getAllEmployee(); System.out.println("----------學(xué)院?jiǎn)T工-----------"); for (CollegeEmployee collegeEmployee : allEmployee) { System.out.println(collegeEmployee.getId()); } } } /** * 學(xué)校管理類(lèi) * * 分析 SchoolManager 類(lèi)的直接朋友類(lèi)有哪些 Employee、CollegeManager * CollegeEmployee 不是 直接朋友,而是一個(gè)陌生類(lèi),這樣違背了迪米特法則 * */ class SchoolManager { /** * 返回學(xué)??偛康膯T工 * @return */ public List<Employee> getAllEmployee() { List<Employee> list = new ArrayList<>(); // 這里我們?cè)黾恿?個(gè)員工到list for (int i = 0; i < 5; i++) { Employee employee = new Employee(); employee.setId("學(xué)校總部員工 id = " + i); list.add(employee); } return list; } /** * 該方法完成輸出學(xué)??偛亢蛯W(xué)院?jiǎn)T工信息 (id) * @param collegeManager */ void printAllEmployee(CollegeManager collegeManager) { // 分析問(wèn)題 // 1. 將輸出學(xué)院的員工方法,封裝到CollegeManager collegeManager.printEmployee(); // 獲取到學(xué)院總部員工 List<Employee> employee = this.getAllEmployee(); System.out.println("-----------學(xué)??偛繂T工-------------"); for (Employee employee1 : employee) { System.out.println(employee1.getId()); } } }
1)迪米特法則的核心是降低類(lèi)之間的耦合
2)但是注意 :由于每個(gè)類(lèi)都減少了不必要的依賴(lài),因此迪米特法則只是要求降低類(lèi)間(對(duì)象間)耦合關(guān)系,并不是要求完全沒(méi)有依賴(lài)關(guān)系。
基本介紹 :原則是盡量使用合成/聚合的方式,而不是使用繼承。
1)找出應(yīng)用中可能需要變換之處,把它們獨(dú)立出來(lái),不要和那些需要變化的代碼混在一起。
2)針對(duì)接口編程,而不是針對(duì)實(shí)現(xiàn)編程。
3)為了交互對(duì)象之間的松耦合設(shè)計(jì)而努力。
讀到這里,這篇“Java設(shè)計(jì)模式七大原則是什么及怎么實(shí)現(xiàn)”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)頁(yè)標(biāo)題:Java設(shè)計(jì)模式七大原則是什么及怎么實(shí)現(xiàn)
文章起源:http://aaarwkj.com/article0/gppsoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、小程序開(kāi)發(fā)、網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站設(shè)計(jì)、標(biāo)簽優(yōu)化、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(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)