設計模式七大原則
阿新 • • 發佈:2020-08-05
設計模式七大原則
- 單一職責原則
- 對類來說,一個類只負責一項職責。比如說A類有兩個職責,職責1,職責2;那麼就可以把A類分為兩個類,這就是遵循了單一職責原則
- 介面隔離原則
- 客戶端類不需要依賴它不需要的介面,既一個類對另一類依賴應該建立在最小介面上
- 簡單的說客戶類,要使用一個介面的時候,不要把用不到的方法也實現了
- 依賴倒轉原則
- 高層模組不要依賴底層模組,二者都要依賴其抽象
- 抽象不要依賴細節,細節應該依賴抽象
- 依賴倒轉(倒置)的中心思想就是面向介面程式設計
- 依賴倒轉原則基於這樣的設計理念;對於細節的多邊形,抽象要穩定的多。以抽象為基礎搭建的架構要比以細節為基礎的架構穩定的多。在Java中抽象就是介面或抽象類,細節就是實體類
- 使用介面或抽象類目的是指定好的規範,而不涉及任何具體操作。把展示細節部分交給他們的實現類完成
- 里氏替換原則
- 在繼承上,儘量不要重寫父類的方法
- 適當情況下,可以使用組合、聚合來代替繼承
- 可以使用父類的地方也可以使用子類
- 開閉原則
OCP
- 開閉原則是最重要的原則
- 對擴充套件開發;對修改關閉。
- 對於增加服務的時候,不會影響已使用的程式碼;
- 迪米特法則
- 合成複用原則
設計模式目的
- 程式碼重用性
- 可讀性
- 可擴充套件性
- 可靠性
- 使程式高內聚,低耦合
單一職責原則
說明
對類來說,一個類只負責一項職責。比如說A類有兩個職責,職責1,職責2;那麼就可以把A類分為兩個類,這就是遵循了單一職責原則
程式碼演示的升級變化
/** * @author HiWin10 * @date 2020/7/29 * 學習單一職責原則,使用交通工具來進行舉例, * 該版本類,是沒有遵循單一職責原則的,應該這個類的公交類很複雜,使用了很多交通工具,解決辦法是,根據交通工具的類別分別建立對應的類 */ public class SingleResponsibility1 { public static void main(String[] args) { Vehicle vehicle = new Vehicle(); vehicle.run("摩托車"); vehicle.run("飛機"); vehicle.run("輪船"); } } /** * 交通工具類 * 沒有遵循單一職責原則,解決辦法是,根據交通工具分別建立對應類 具體看SingleResponsibility2 */ class Vehicle { public void run (String vehicle) { System.out.println(vehicle + "在公路上執行...."); } }
/**
* @author HiWin10
* @date 2020/7/29
* 1. 遵守了單一職責原則,
* 2. 但是這樣改動很大,將類分解,還要修改客戶端
* 3. 直接修改Vehicle,對於咱們目前的場景,這個類只有一個功能的話,那麼可以有方法單一職責具體看SingleResponsibility3,
* 當然SingleResponsibility3具體要看需求的
*/ public class SingleResponsibility2 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("摩托");
AriVehicle ariVehicle = new AriVehicle();
ariVehicle.run("飛機");
WaterVehicle waterVehicle = new WaterVehicle();
waterVehicle.run("輪船");
}
}
class RoadVehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在公路上執行....");
}
}
class AriVehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在天空執行....");
}
}
class WaterVehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在水裡執行....");
}
}
/**
* @author HiWin10
* @date 2020/7/29
* 1. 這種方式沒有對SingleResponsibility1而言沒有大的修改,只是增加方法
* 2. 這種方式雖然對類沒有遵循單一職責原則,但是在方法上,遵循了單一職業原則
* 3. 所以說即使是設計模式,也沒有說必須的套路,要根據實際應用來使用
*/ public class SingleResponsibility3 {
public static void main(String[] args) {
Vehicle3 vehicle = new Vehicle3();
vehicle.run("摩托");
vehicle.runAir("飛機");
vehicle.runWater("輪船");
}
}
class Vehicle3 {
public void run(String vehicle) {
System.out.println(vehicle + "在公路上跑....");
}
public void runAir(String vehicle) {
System.out.println(vehicle + "在天空執行....");
}
public void runWater(String vehicle){
System.out.println(vehicle + "在水中執行....");
}
}
小結
- 降低類的複雜度,一個類只負責意見事情
- 提高程式碼的可讀性和維護性,因為一個類只做一件事情,那麼這個類可以說是很簡單
- 修改其他有關程式碼,不會影響其他程式碼
- 通常情況下,我們應對遵循單一職責原則,只有在程式碼邏輯十分簡單,可以不遵守或者可以在方法級別上遵守
介面隔離原則
概念
客戶端類不應該依賴它不要的介面,既一個類對另一個類的依賴應該建立在最小介面上
簡答的說就是不需要的類不要依賴,不要的方法不要實現。
- 該圖片中,A類使用B類的
1,2,3方法
;C類使用D類的1,4,5方法
;B類和D類分別都實現了Interface1
的所有方法。 - 那麼根據介面隔離原則來講,使用不到的不實現。也就是說B類不需要實現
Interface1
的4,5方法,D類不要實現Interface1
的2,3方法 - 解決辦法:將
Interface1
介面中方法分離,1
方法單獨一個介面,2,3
單獨一個介面,4,5
單獨一個介面 - 根據實際情況來定,這個介面隔離原則還可以使用抽象類來解決,或者普通的類也可以解決。
下面是沒有遵循介面隔離原則的場景模擬
/**
* @author HiWin10
* @date 2020/7/31
* 介面隔離原則模擬
* 介面隔離就是一個介面有很多方法,每個實現都要排除不必要的方法
*/ public class Segregation1 {
public static void main(String[] args) {
A a = new A();
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
System.out.println("-----------------------");
C c = new C();
c.depend1(new D());
c.depend4(new D());
c.depend5(new D());
}
}
/**
* 介面有5個方法,全部都要實現
*/ interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5(); }
/**
* 介面的實現,B,但是A類只會依賴B類(使用),使用B類的1,2,3,方法
*/ class B implements Interface1{
@Override
public void operation1() {
System.out.println("B 實現了Interface1的 operation1方法 ");
}
@Override
public void operation2() {
System.out.println("B 實現了Interface1的 operation2方法 ");
}
@Override
public void operation3() {
System.out.println("B 實現了Interface1的 operation3方法 ");
}
@Override
public void operation4() {
System.out.println("B 實現了Interface1的 operation4方法 ");
}
@Override
public void operation5() {
System.out.println("B 實現了Interface1的 operation5方法 ");
}
}
/**
* 介面的實現D,但是C類只會依賴D類(使用),使用D類的1,4,5,方法
*/ class D implements Interface1 {
@Override
public void operation1() {
System.out.println("D 實現了Interface1的 operation1方法 ");
}
@Override
public void operation2() {
System.out.println("D 實現了Interface1的 operation2方法 ");
}
@Override
public void operation3() {
System.out.println("D 實現了Interface1的 operation3方法 ");
}
@Override
public void operation4() {
System.out.println("D 實現了Interface1的 operation4方法 ");
}
@Override
public void operation5() {
System.out.println("D 實現了Interface1的 operation5方法 ");
}
}
/**
* A類通過介面依賴B類(使用B類,只會使用1,2,3方法)
* 通過這個就可以看到,我只想使用介面的三個方法,但是B類卻實現了Interface1介面的所有方法
* 這樣的話對於A類來說4,5方法都是使用不到的,卻實現了,從這裡看程式碼就非常多餘
*/ class A {
public void depend1(Interface1 i){
i.operation1();
}
public void depend2(Interface1 i){
i.operation2();
}
public void depend3(Interface1 i){
i.operation3();
}
}
class C { // C類通過介面依賴D類(使用D類,只會使用1,4,5方法)
public void depend1(Interface1 i){
i.operation1();
}
public void depend4(Interface1 i){
i.operation4();
}
public void depend5(Interface1 i){
i.operation5();
}
}
遵循介面隔離原則
package com.itcast.介面隔離原則; /**
* @author HiWin10
* @date 2020/7/31
* 介面隔離原則模擬
* 介面隔離就是一個介面有很多方法,每個實現都要排除不必要的方法
* 解決辦法:
* 將幾個方法根據使用不同來拆除,比如說A類使用1,2,3方法;C類使用1,4,5方法,那麼1方法就是1個類,2,3一類,4,5一個類
* 也可以使用抽象類解決該問題
*/ public class Segregation2 {
public static void main(String[] args) {
A2 a = new A2();
a.depend1(new B2());
a.depend2(new B2());
a.depend3(new B2());
System.out.println("------------------");
C2 c = new C2();
c.depend1(new D2());
c.depend4(new D2());
c.depend5(new D2()); }
}
/**
* 將介面方法根據使用來拆除,後期實現可以根據實際情況使用抽象類解決該問題
*/ interface Interface2 {
void operation1(); }
interface Interface3 {
void operation2();
void operation3(); }
interface Interface4 {
void operation4();
void operation5(); }
class B2 implements Interface2, Interface3 {
@Override
public void operation1() {
System.out.println("B2 實現了 Interface2的operation1方法");
}
@Override
public void operation2() {
System.out.println("B2 實現了 Interface2的operation2方法");
}
@Override
public void operation3() {
System.out.println("B2 實現了 Interface2的operation3方法");
}
}
class D2 implements Interface2, Interface4 {
@Override
public void operation1() {
System.out.println("D2 實現了 Interface2的operation1方法");
}
@Override
public void operation4() {
System.out.println("D2 實現了 Interface2的operation4方法");
}
@Override
public void operation5() {
System.out.println("D2 實現了 Interface2的operation5方法");
}
}
class A2 {
public void depend1(Interface2 i){
i.operation1();
}
public void depend2(Interface3 i){
i.operation2();
}
public void depend3(Interface3 i){
i.operation3();
}
}
class C2 {
public void depend1(Interface2 i){
i.operation1();
}
public void depend4(Interface4 i){
i.operation4();
}
public void depend5(Interface4 i){
i.operation5();
}
}
依賴倒轉原則
- 高層模組不要依賴底層模組,二者都要依賴其抽象
- 抽象不要依賴細節,細節應該依賴抽象
- 依賴倒轉(倒置)的中心思想就是面向介面程式設計
- 依賴倒轉原則基於這樣的設計理念;對於細節的多邊形,抽象要穩定的多。以抽象為基礎搭建的架構要比以細節為基礎的架構穩定的多。在Java中抽象就是介面或抽象類,細節就是實體類
- 使用介面或抽象類目的是指定好的規範,而不涉及任何具體操作。把展示細節部分交給他們的實現類完成
不遵循依賴倒轉原則的程式碼
/**
* @author HiWin10
* @date 2020/7/31
* 依賴倒轉原則
* 1. 高層模組不要依賴底層模組,二者都要依賴其抽象
* 2. 抽象不要依賴細節,細節應該依賴抽象
* 3. 依賴倒轉(倒置)的中心思想就是面向介面程式設計
* 4. 依賴倒轉原則基於這樣的設計理念;對於細節的多邊形,抽象要穩定的多。
* 以抽象為基礎搭建的架構要比以細節為基礎的架構穩定的多。在Java中抽象就是介面或抽象類,細節就是實體類
* 5. 使用介面或抽象類目的是指定好的規範,而不涉及任何具體操作。把展示細節部分交給他們的實現類完成
*
* 案例:就是接受訊息的一個案例
*/ public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
/**
* 訊息類
*/ class Email {
String getInfo(){
return "郵箱訊息:Hello, World!";
}
}
/**
* 分析:
* 1. 簡單,比較容易想到
* 2. 缺乏靈活性,如果我們傳送Person類的訊息不是Email的了,而是微信訊息那麼該如何操作?這種寫法的話,程式碼寫死了缺乏靈活性
* 解決思路:中間定義一個抽象層,方法引數為抽象層,實體類實現了抽象層,這樣來呼叫就會靈活的多
*/ class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}
遵循依賴倒轉原則
/**
* @author HiWin10
* @date 2020/7/31 */ public class DependecyInversion_OK {
public static void main(String[] args) {
Person1 person = new Person1();
// 傳送電子郵箱訊息
person.receive(new Email1()); // 傳送微信訊息
person.receive(new Weixin()); }
}
interface IReceive {
public String getInfo(); }
/**
* 電子郵件訊息類
*/ class Email1 implements IReceive {
@Override
public String getInfo(){
return "郵箱訊息:Hello, World!";
}
}
/**
* 微笑訊息類
*/ class Weixin implements IReceive {
@Override
public String getInfo() {
return "微信訊息:Are you OK";
}
}
class Person1{
public void receive(IReceive receive){
System.out.println(receive.getInfo());
}
}
依賴倒轉原則關係傳遞的三種方式
- 構造器傳遞
setter
方法傳遞- 方法介面傳遞
程式碼示例
/**
* @author HiWin10
* @date 2020/7/31
* 依賴倒轉原則三種傳遞方式
*/ public class DependencyPass {
}
// 方式1:通過介面傳遞 // 開關的介面 interface IOpenAndClose {
public void open(ITV itv); // 抽象方法,接受介面 }
/**
* 介面
*/ interface ITV {
public void play(); }
/**
* 電視的實現
*/ class ChangHong implements ITV {
@Override
public void play() {
System.out.println("長虹電視開機了...");
}
}
/**
* 操作類的實現
*/ class OpenAndClose implements IOpenAndClose {
@Override
public void open(ITV itv) {
itv.play();
}
}
/*******************/ // 方式2:構造器傳遞 interface IOpenAndClose2 {
public void open(ITV itv); // 抽象方法,接受介面 }
/**
* 介面
*/ interface ITV2 {
public void play(); }
class ChangHong2 implements ITV2 {
@Override
public void play() {
System.out.println("長虹電視開機了...");
}
}
/**
* 構造器方式
*/ class OpenAndClose2 implements IOpenAndClose2 {
private ITV2 itv2; public OpenAndClose2(ITV2 itv2) {
this.itv2 = itv2;
}
@Override
public void open(ITV itv) {
}
}
/*******************/ // 方式3:set方法 interface IOpenAndClose3 {
public void open(ITV itv); // 抽象方法,接受介面 }
/**
* 介面
*/ interface ITV3 {
public void play(); }
class ChangHong3 implements ITV3 {
@Override
public void play() {
System.out.println("長虹電視開機了...");
}
}
/**
* set方法
*/ class OpenAndClose3 implements IOpenAndClose3 {
private ITV3 itv3; public void setItv3(ITV3 itv3) {
this.itv3 = itv3;
}
@Override
public void open(ITV itv) {
this.itv3.play();
}
}
小結
- 底層模組儘量都要有抽象或介面。或者兩者都有,程式穩定性會更好
- 變數的宣告型別儘量是抽象類或介面,這樣我們的變數引用和實際物件間,就存在一個緩衝層,利於程式擴充套件和優化
- 繼承時遵循里氏替換原則
里氏替換原則
繼承帶來的優劣勢
-
繼承包含這樣一個含義:父類中凡是已經實現好的方法,實際上是在設定規範和契約,雖然沒有強制所有子類必須遵循這些契約,但是如果子類隨意重寫父類實現的方法,就會對繼承體系造成破壞
-
繼承在給程式帶來便利同時,也會有弊端。比如使用繼承會給程式帶來侵入性,程式的可移植性減低,增加物件的耦合,如果一個類被其他類所繼承,則這個類需要修改時,必須考慮所有子類,並且父類修改後,所有涉及到子類的功能都有可能產生故障
-
問題提示:在程式設計中,如何正確的使用繼承?=> 里氏替換原則
里氏替換原則概念
- 如果對每個型別為T1的物件o1,都有型別為
T2
的物件o2
,使得以T1
定義的所有程式P
在所有物件o1
都代還成o2
時,程式P的行為沒有發生變化,那麼T2
是型別T1
的子類。或句話說,所有引用基類的地方必須能透明地使用其子類物件 - 使用繼承時,遵循里氏替換原則,在子類中儘量不要重寫父類的方法
- 里氏替換原則告訴我們,繼承實際上讓兩個類的耦合提高了,在適當情況下,可以通過聚合、組合、依賴來解決問題
問題產出
/**
* @author HiWin10
* @date 2020/8/3
* 里氏替換原則
* 里氏替換原則需要滿足一下幾點:
* 1. 所有使用基類的地方都可以使用子類來代替
* 2. 儘量不要重寫父類的方法
* 3. 在適當情況下,可以選擇使用聚合、組合、依賴來解決問題
*/ public class Liskow1 {
public static void main(String[] args) {
A a = new A();
System.out.println("20-10=" + a.func(20, 10));
System.out.println("20-18=" + a.func(20, 18)); System.out.println("------------");
B b = new B();
System.out.println("11-3=" + b.func(10, 20));
System.out.println("20-18+1=" + b.fun2(20, 18)); }
}
class A {
/**
* 減法
*/
public Integer func(int a, int b){
return a - b;
}
}
// 按照main中B物件的邏輯,是想要使用A的方法,但是B類不小心重寫了A的其中方法,所以說才會發生此事,計算失誤。違反了里氏替換原則 // 解決辦法:使用聚合、組合、依賴來解決問題 class B extends A {
/**
* B類不小心重寫了 A類的方法
*/
public Integer func(int a, int b) {
return a + b;
}
public Integer fun2(int a, int b) {
return func(a, b) + 1;
}
}
- A類有一個方法
func
作用減法。 - B類繼承A類想要在
main
中使用func
但是不小心重寫了A類的func
方法,所以在使用的時候邏輯上不正確 - 解決方法就是使用使用聚合、組合、依賴來解決問題
程式碼如下:
/**
* @author HiWin10
* @date 2020/8/3
* 里氏替換原則
* 里氏替換原則需要滿足一下幾點:
* 1. 所有使用基類的地方都可以使用子類來代替
* 2. 儘量不要重寫父類的方法
* 3. 在適當情況下,可以選擇使用聚合、組合、依賴來解決問題
*/ public class Liskow2 {
public static void main(String[] args) {
A1 a = new A1();
System.out.println("20-10=" + a.func(20, 10));
System.out.println("20-18=" + a.func(20, 18)); System.out.println("------------");
B1 b = new B1();
System.out.println("11-3=" + b.func(11, 3));
System.out.println("20-18+1=" + b.fun2(20, 18)); }
}
class A1 {
/**
* 減法
*/
public Integer func(int a, int b){
return a - b;
}
}
// 按照main中B物件的邏輯,是想要使用A的方法,但是B類不小心重寫了A的其中方法,所以說才會發生此事,計算失誤。違反了里氏替換原則 // 解決辦法:使用聚合、組合、依賴來解決問題/本次使用組合來解決 class B1 {
private A1 a = new A1(); /**
* B類不小心重寫了 A類的方法
*/
public Integer func(int a, int b) {
// return a + b;
return this.a.func(a, b);
}
public Integer fun2(int a, int b) {
return this.a.func(a, b) + 1;
}
}
開閉原則[最重要]
概念
- 開閉原則是程式設計中最基礎、最重要的原則
- 一個軟體實體如類,模組和函式應該對擴充套件開發(對提供方),對修改關閉(對使用方)。由抽象構建框架,用實現擴充套件細節。
- 當軟體需要變化時,儘量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有程式碼實現變化。
- 程式設計中遵循其他原則,以及使用設計模式的目的就是遵循開閉原則
程式碼示例
該程式碼是,通過方法來決定繪畫那種圖形,這裡是沒有遵循開閉原則的,你會發現,再新增一個圖形的時候,不管是服務方還是使用方都要修改或擴充套件程式碼
/**
* @author HiWin10
* @date 2020/8/3
* 開閉原則
* 1. 對功能擴充套件開放,對修改關閉
* 2. 儘量使用軟體的實體行為實現變化,而不是修改已有程式碼
*/ public class Ocp1 {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle()); // 繪畫新新增的三角形
graphicEditor.drawShape(new Triangle());
}
}
// 沒有遵循開閉原則,當要新增一個圖形的時候,要修改的東西太多, // 首先,服務需要修改 其次使用方也要修改程式碼 // [使用方] // 解決辦法:新增抽象層,新增好規範好,由具體實現來完成具體圖形的繪畫
class GraphicEditor {
public void drawShape(Shape s){
if (s.m_type == 1){
drawRectangle(s);
} else if (s.m_type == 2) {
drawCircle(s);
} // 首先這裡修改 新增的三角形
else if (s.m_type == 3){
drawTriangle(s);
}
}
// 繪製矩形
private void drawRectangle(Shape s) {
System.out.println(" 繪製矩形 ");
}
// 繪製圓形
private void drawCircle(Shape s) {
System.out.println(" 繪製圓型 ");
}
// 這裡修改新增新方法drawTriangle畫三角形
private void drawTriangle(Shape s){
System.out.println("繪製 三角形");
}
}
class Shape {
int m_type; }
// 矩形 class Rectangle extends Shape {
public Rectangle() {
super.m_type = 1;
}
}
// 圓形 class Circle extends Shape {
public Circle() {
super.m_type = 2;
}
}
// 新新增三角形 [服務方] class Triangle extends Shape {
public Triangle() {
super.m_type = 3;
}
}
問題
- 雖然對提供方是擴充套件(新增類),對使用方還是修改
- 解決思路:中間新增抽象層,然後圖形的繪畫交給具體實現,而不是由使用方來繪畫
/**
* @author HiWin10
* @date 2020/8/3
* 開閉原則
* 1. 對功能擴充套件開放,對修改關閉
* 2. 儘量使用軟體的實體行為實現變化,而不是修改已有程式碼
*
* 開始使用開閉原則
* 新增抽象類,定義介面 然後每個實現來繪畫物件圖形
*/ public class Ocp2 {
public static void main(String[] args) {
GraphicEditor1 graphicEditor = new GraphicEditor1();
graphicEditor.drawShape(new Rectangle1());
graphicEditor.drawShape(new Circle1()); // 繪畫新新增的三角形
graphicEditor.drawShape(new Triangle1());
}
}
class GraphicEditor1 {
public void drawShape(Shape2 s){
s.drawShape();
}
}
abstract class Shape2 {
int m_type;
public abstract void drawShape(); }
// 矩形 class Rectangle1 extends Shape2 {
public Rectangle1() {
super.m_type = 1;
}
@Override
public void drawShape() {
System.out.println(" 繪製矩形 ");
}
}
// 圓形 class Circle1 extends Shape2 {
public Circle1() {
super.m_type = 2;
}
@Override
public void drawShape() {
System.out.println(" 繪製圓型 ");
}
}
// 新新增三角形 class Triangle1 extends Shape2 {
public Triangle1() {
super.m_type = 3;
}
@Override
public void drawShape() {
System.out.println("繪製 三角形");
}
}
小結
以後在設計程式儘量使用抽象,這樣的話程式會非常靈活。但是同樣的程式也會非常負責。
迪米特法則
概念
- 一個物件應該對其他物件保持最少了解
- 類與類的關係越密切,耦合度越高
- 迪米特法則,又叫最少知道原則。既一個類對自己依賴的類知道的越少越好。也就是說。對於依賴的類不管多麼複雜,都要儘量將邏輯封裝在類的內部,對外除了提供的
public
方法,不對外洩露任何資訊 - 迪米特法則還有更簡單的定義:只與直接的朋友通訊
- 直接朋友:每個物件都會與其他物件有耦合關係,只要連個物件之間有耦合關係,我們就說這兩個物件是朋友關係。我們稱出現成員變數、方法引數、方法返回值就是直接朋友,而在區域性出現的類就不是直接朋友。也就是說,陌生的類最好不要以區域性變數方式存在類中
耦合關係分別有:依賴,關聯,組合,聚合等。
程式碼示例
- 有一個學校,下屬有各個學院和總部,現要求打印出學校總部員工ID和學院員工ID
- 程式碼演示
/**
* @author HiWin10
* @date 2020/8/4
* 迪米特法則
* 1. 一個物件應該對其他物件保持最少的瞭解
* 2. 類與類關係密切,耦合度就越高
* 3. 迪米特法則又叫最少知道原則,既一個類對自己依賴的類知道越少越好,也就是說,對被依賴的類不管多麼複雜,都要儘量將邏輯封裝在類內部,對外提供public方法,不對外洩露
* 4. 迪米特法則還有更簡單定義:只與直接朋友通訊
*
* 直接朋友:該類的屬性,或者setter方法;或者方法引數
*/ public class Demeter1 {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
}
}
// 學院總員工類 class Employee {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
'}';
}
}
// 學院的員工類 class CollegeEmployee {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "CollegeEmployee{" +
"id='" + id + '\'' +
'}';
}
}
// 管理學院員工的管理類 class CollegeManager {
// 返回學院所有員工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("學院員工id = " + i);
list.add(emp);
}
return list;
}
}
// 學院管理類 // 分析:該類的直接朋友:Employee、CollegeManager // CollegeEmployee 類不是直接朋友違背了迪米特法則。將這一段程式碼移動到該類實現上去 class SchoolManager {
// 返回學院總部的員工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee emp = new Employee();
emp.setId("學院總員工ID = " + i);
list.add(emp);
}
return list;
}
// 該方法返回輸出學院總部和學院員工資訊
public void printAllEmployee(CollegeManager sub) {
// 獲取到學院員工
// 解決辦法:將這一段程式碼移動到該類實現上去,
List<CollegeEmployee> list = sub.getAllEmployee();
System.out.println("-------學院員工-------");
list.forEach(System.out::println); // 獲取學院總部員工
List<Employee> employeeList = this.getAllEmployee();
System.out.println("-------學院總員工-------");
employeeList.forEach(System.out::println);
}
}
問題發現
- CollegeEmployee 類不是直接朋友違背了迪米特法則。將這一段程式碼移動到該類實現上
/**
* @author HiWin10
* @date 2020/8/4
* 迪米特法則
* 1. 一個物件應該對其他物件保持最少的瞭解
* 2. 類與類關係密切,耦合度就越高
* 3. 迪米特法則又叫最少知道原則,既一個類對自己依賴的類知道越少越好,也就是說,對被依賴的類不管多麼複雜,都要儘量將邏輯封裝在類內部,對外提供public方法,不對外洩露
* 4. 迪米特法則還有更簡單定義:只與直接朋友通訊
*
* 直接朋友:該類的屬性,或者setter方法;或者方法引數
*/ public class Demeter2 {
public static void main(String[] args) {
SchoolManager2 schoolManager = new SchoolManager2();
schoolManager.printAllEmployee(new CollegeManager2());
}
}
// 學院總員工類 class Employee2 {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
'}';
}
}
// 學院的員工類 class CollegeEmployee2 {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "CollegeEmployee{" +
"id='" + id + '\'' +
'}';
}
}
// 管理學院員工的管理類 class CollegeManager2 {
// 返回學院所有員工
public List<CollegeEmployee2> getAllEmployee() {
List<CollegeEmployee2> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee2 emp = new CollegeEmployee2();
emp.setId("學院員工id = " + i);
list.add(emp);
}
return list;
}
// 顯示移動到改方法中
public void printAllemployee() {
List<CollegeEmployee2> list = this.getAllEmployee();
System.out.println("-------學院員工-------");
list.forEach(System.out::println);
}
}
// 學院管理類 // 分析:該類的直接朋友:Employee、CollegeManager // CollegeEmployee 類不是直接朋友違背了迪米特法則。將這一段程式碼移動到該類實現上去 class SchoolManager2 {
// 返回學院總部的員工
public List<Employee2> getAllEmployee() {
List<Employee2> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee2 emp = new Employee2();
emp.setId("學院總員工ID = " + i);
list.add(emp);
}
return list;
}
// 該方法返回輸出學院總部和學院員工資訊
public void printAllEmployee(CollegeManager2 sub) {
// 獲取到學院員工
// 解決辦法:將這一段程式碼移動到該類實現上去,
sub.printAllemployee(); // 獲取學院總部員工
List<Employee2> employeeList = this.getAllEmployee();
System.out.println("-------學院總員工-------");
employeeList.forEach(System.out::println);
}
}
合成複用原則
就是儘量以合成/聚合的方式,而不是使用繼承
設計模式總結
- 找到應用中可能要變化之處,把他們獨立出來,不要和那些不需要變化的程式碼混在一起
- 針對介面程式設計。而不是針對實現程式設計
- 為了互動物件之間鬆耦合設計而努力