設計模式七大原則

Java設計模式七大原則

懂了設計模式,你就懂了面向對象分析和設計
(OOA/D)的精要


1. 設計模式的重要性

  1. 軟件工程中,設計模式(design pattern)是對軟件設計中普遍存在(反覆出現)
    的各種問題,所提出的解決方案。
  2. 實際工作經歷來說, 當一個項目開發完後,如果客戶提出增新功能,怎麼辦?
  3. 原來程序員離職,你接手維護該項目怎麼辦? (維護性[可讀性、規範性])

2.設計模式的目的

  1. 代碼重用性 (即:相同功能的代碼,不用多次編寫
  2. 可讀性 (即:編程規範性, 便於其他程序員的閱讀和理解)
  3. 可擴展性 (即:當需要增加新的功能時,非常的方便,稱爲可維護
  4. 可靠性 (即:當我們增加新的功能後,對原來的功能沒有影響)
  5. 使程序呈現高內聚,低耦合的特性

3.設計模式常用七大原則

單一職責原則

接口隔離原則

依賴倒轉(倒置)原則

里氏替換原則

開閉原則

迪米特法則

合成複用原則

3.1單一職責原則

基本介紹:
對類來說的,即一個類應該只負責一項職責。如類A負責兩個不同職責:職責1,職責2。 當職責1需求變更而改變A時,可能造成職責2執行錯誤,所以需要將類A的粒度分解爲 A1,A2
應用實例:
方案一:

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 單一職責原則01 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托車");
        vehicle.run("汽車");
        vehicle.run("飛機");
    }
}

//交通工具類
class Vehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在路上跑");
    }
}
分析:
  1. 在方式1的run方法中,違反了單一職責原則
  2. 解決的方案非常的簡單,根據交通工具運行方法不同,分解成不同類即可

方案二:

package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 單一職責原則01 {
    public static void main(String[] args) {

        AirVehicle airVehicle = new AirVehicle();
        airVehicle.run("飛機");
    }
}


//天空工具類
class AirVehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在天上飛");
    }
}

//陸地工具類
class RodaVehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在路上跑");
    }
}

//海洋工具類
class WaterVehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在水上游");
    }
}
分析:
  1. 遵守單- -職責原則
  2. 但是這樣做的改動很大,即將類分解,同時修改客戶端
  3. 改進:直接修改Vehicle 類,改動的代碼會比較少=>方案3

方案三:


//交通工具類
class Vehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"跑");
    }
    public void Roadrun(String vehicle){
        System.out.println(vehicle+"在路上跑");
    }
    public void Waterrun(String vehicle){
        System.out.println(vehicle+"在水裏遊");
    }
    public void Airrun(String vehicle){
        System.out.println(vehicle+"在天上飛");
    }
}
分析:

1.這種修改方法沒有對原來的類做大的修改,只是增加方法
2. 這裏雖然沒有在類這個級別上遵守單- -職責原則,但是在方法級別上,仍然是遵守單- -職責

單一職責原則注意事項和細節小結:

1)降低類的複雜度, 一個類只負責-項職責。
2)提高類的可讀性,可維護性
3)降低變更引起的風險
4)通常情況下,我們應當遵守單一職責原則,只有邏輯足夠簡單,纔可以在代碼級違反單- -職責原則;只有類中方法數量足夠少,可以在方法級別保持單- -職責原則

3.2接口隔離原則

基本介紹:
客戶端不應該依賴它不需要的接口,即一個類對另一個類的依賴應該建立在最小的接口上

在這裏插入圖片描述

圖片解釋:

類A通過接口Interface1依賴類B,類C通過接口Interface1依賴類D,如果接口Interface1對於類A和類C來說不是最小接口,那麼類B和類D必須去實現他們不需要的方法。


按隔離原則應當這樣處理:將接口Interface1拆分爲獨立的幾個接口,類A和類C分別與他們需要的接口建立依賴關係。也就是採用接口隔離原則


應用實例:

不遵守接口隔離原則

package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 接口隔離原則 {
    public static void main(String[] args) {
        A  a = new A();
        a.run(new D());//此時不需要實現類D的D E方法
        C c=new C();
        c.run(new B());//此時不需要實現類B的B C方法
    }
}
interface Interafce{
    void A();
    void B();
    void C();
    void D();
    void E();
}
class D implements Interafce{
    @Override
    public void A() {
        System.out.println("D類實現Father接口方法A");
    }
    @Override
    public void B() {
        System.out.println("D類實現Father接口方法B");
    }
    @Override
    public void C() {
        System.out.println("D類實現Father接口方法c");
    }
    @Override
    public void D() {
        System.out.println("D類實現Father接口方法D");
    }

    @Override
    public void E() {
        System.out.println("D類實現Father接口方法E");
    }
}
class B implements Interafce{
    @Override
    public void A() {
        System.out.println("c類實現Father接口方法A");
    }
    @Override
    public void B() {
        System.out.println("c類實現Father接口方法B");
    }
    @Override
    public void C() {
        System.out.println("c類實現Father接口方法c");
    }
    @Override
    public void D() {
        System.out.println("c類實現Father接口方法D");
    }

    @Override
    public void E() {
        System.out.println("c類實現Father接口方法E");
    }
}
/*
* 類A的參數傳入了接口,實例化對象時,假如想使用C類的方法,將C類傳入,此時C類已經實現接口
* */
class A{
    void runA(Interafce d){
     d.A();
    }
    void runB(Interafce d){
     d.B();
    }
    void runC(Interafce d){
     d.C();
    }
}
class C{
    void run(Interafce d){
        d.A();
    }
   void runD(Interafce d){
     d.D();
    }
    void runE(Interafce d){
     d.E();
    }
}
不遵守接口隔離原則出現問題:
應傳統方法的問題和使用接口隔離原則改進.
  • 類A通過接口Interfacel 依賴類B,類C通過接口Interfacel依賴類D,如果接口Interfacel 對於類A和類C來說不是最小接口,那麼類B和類D必須去實現他們不需要的方法
  • 將接口 Interface1拆分爲獨立的幾個接口,類A和類C分別與他們需要的接口建立依賴關係。也就是採用接口隔離原則
  • 接口Interfacel 中出現的方法,根據實際情況拆分爲三個接口
遵守接口隔離原則代碼實現:
package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 接口隔離原則 {
    public static void main(String[] args) {
        A  a = new A();
        a.runA(new D());
        a.runB(new D());
        a.runC(new D());
        System.out.println("***************");
        C c=new C();
        c.runA(new B());
        c.runD(new B());
        c.runE(new B());
    }
}
interface Interafce{
    void A();
}
interface Interafce2{
    void B();
    void C();
}
interface Interafce3{
    void D();
    void E();
}
class D implements Interafce,Interafce2{
    @Override
    public void A() {
        System.out.println("D類實現Father接口方法A");
    }
    @Override
    public void B() {
        System.out.println("D類實現Father接口方法B");
    }
    @Override
    public void C() {
        System.out.println("D類實現Father接口方法c");
    }
}
class B implements Interafce,Interafce3{
    @Override
    public void A() {
        System.out.println("c類實現Father接口方法A");
    }
    @Override
    public void D() {
        System.out.println("c類實現Father接口方法D");
    }

    @Override
    public void E() {
        System.out.println("c類實現Father接口方法E");
    }
}
/*
* 類A的參數傳入了接口,實例化對象時,假如想使用C類的方法,將C類傳入,此時C類已經實現接口
* */
class A{
    void runA(Interafce d){
        d.A();
    }
    void runB(Interafce2 d){
        d.B();
    }
    void runC(Interafce2 d){
        d.C();
    }
}
class C{
    void runA(Interafce d){
        d.A();
    }
    void runD(Interafce3 d){
        d.D();
    }
    void runE(Interafce3 d){
        d.E();
    }
}
代碼結果:

在這裏插入圖片描述

3.3依賴倒轉原則

基本介紹:
  1. 高層模塊不應該依賴低層模塊,二者都應該依賴其抽象
  2. 抽象不應該依賴細節,細節應該依賴抽象
  3. 依賴倒轉(倒置)的中心思想是面向接口編程
  4. 依賴倒轉原則是基於這樣的設計理念:相對於細節的多變性,抽象的東西要穩定的
    多。以抽象爲基礎搭建的架構比以細節爲基礎的架構要穩定的多。在java中,抽象
    指的是接口或抽象類,細節就是具體的實現類
  5. 使用接口或抽象類的目的是制定好規範,而不涉及任何具體的操作,把展現細節的
    任務交給他們的實現類去完成
應用實例:
package DesignPattern.Test;
/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 依賴倒轉原則 {
    public static void main(String[] args) {
        Person person = new Person();
        person.Recive(new Email());
    }
}

class Person {
    void Recive(Email email) {
        System.out.println("接收到的");
        email.getInfo();
    }
}

class Email {
    void getInfo() {
        System.out.println("信息內容: 你好我是小明!");
    }
}

分析:

完成Person接收消息的功能

  1. 簡單,比較容易想到
  2. 如果我們獲取的對象是微信,短信等等,則新增類,同時Perons也要增加相應的接收方法
  3. 解決思路:引入一個抽象的接口IReceiver, 表示接收者,這樣Person類與接口IReceiver發生依賴因爲 Email, WeiXin等等屬於接收的範圍,他們各自實現IReceiver 接口就ok,這樣我們就符號依賴倒轉原則
遵守依賴倒轉原則代碼實現
package DesignPattern.Test;
/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 依賴倒轉原則 {
    public static void main(String[] args) {
        Person person = new Person();
        person.Recive(new Email());
        person.Recive(new Weixin());
    }
}
/*
* 定義接口
**/
interface  IReceiver{
    public void getInfo();
}

class Email implements IReceiver{

    public void getInfo() {
        System.out.println("郵件信息內容: 你好我是小明!");
    }
}


class Weixin implements IReceiver{

    public void getInfo() {
        System.out.println("微信信息內容: 你好我是小紅!");
    }
}

class Person {
    void Recive(IReceiver iReceiver) {
        System.out.println("接收到的");
        iReceiver.getInfo();
    }
}

依賴關係傳遞的三種方式和應用案例:
  • 接口傳遞

  • 構造方法傳遞

  • setter方式傳遞

接口傳遞依賴關係案例:
package DesignPattern.Test;

import com.sym.JVM.T;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 依賴關係傳遞 {
    public static void main(String[] args) {
        MyOpenAndClose myOpenAndClose =new MyOpenAndClose();
        myOpenAndClose.open(new XiaoMi());
    }
}
/*
* 開機與關機的接口
* */
interface OpenAndClose{
    /*
    * 開機:參數傳入一個接口  電視
    * 抽象方法,接收接口
    * */
    public void open(TV tv);
}
/*
 *電視接口
 */
interface TV{
    public  void play(); //表示電視機已打開
}

class XiaoMi implements TV{
    @Override
    public void play() {
        System.out.println("小米電視已打開。。");
    }
}
class MyOpenAndClose implements OpenAndClose{
    @Override
    public void open(TV tv) {
        tv.play();
    }
}
構造方法傳遞依賴關係案例:
package DesignPattern.Test;

import com.sym.JVM.T;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 依賴關係傳遞 {
    public static void main(String[] args) {
        MyOpenAndClose myOpenAndClose = new MyOpenAndClose(new XiaoMi());

        myOpenAndClose.open();
    }
}

/*
 * 開機與關機的接口
 * */
interface OpenAndClose {
    /*
     * 開機:參數傳入一個接口  電視
     * 抽象方法,接收接口
     * */
    public void open();
}

/*
 *電視接口
 */
interface TV {
    public void play(); //表示電視機已打開
}

class XiaoMi implements TV {
    @Override
    public void play() {
        System.out.println("小米電視已打開。。");
    }
}

class MyOpenAndClose implements OpenAndClose {
    public TV tv;

    public MyOpenAndClose(TV tv) {
        this.tv = tv;
    }

    @Override
    public void open() {
        this.tv.play();
    }
}
setter傳遞依賴關係案例:
package DesignPattern.Test;

import com.sym.JVM.T;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 依賴關係傳遞 {
    public static void main(String[] args) {
        MyOpenAndClose myOpenAndClose = new MyOpenAndClose();
        myOpenAndClose.setTv(new XiaoMi());
        myOpenAndClose.open();
    }
}

/*
 * 開機與關機的接口
 * */
interface OpenAndClose {
    /*
     * 開機:參數傳入一個接口  電視
     * 抽象方法,接收接口
     * */
    public void open();

    public void setTv(TV tv);
}

/*
 *電視接口
 */
interface TV {
    public void play(); //表示電視機已打開
}

class XiaoMi implements TV {
    @Override
    public void play() {
        System.out.println("小米電視已打開。。");
    }
}

class MyOpenAndClose implements OpenAndClose {
    private TV tv;


    @Override
    public void open() {
        this.tv.play();
    }

    @Override
    public void setTv(TV tv) {
        this.tv = tv;
    }
}
小結:
  1. 低層模塊儘量都要有抽象類或接口,或者兩者都有,程序穩定性更好.
  2. 變量的聲明類型儘量是抽象類或接口, 這樣我們的變量引用和實際對象間,就存在
    一個緩衝層,利於程序擴展和優化
  3. 繼承時遵循里氏替換原則

3.4里氏替換原則

oo中的繼承性的思考和說明:
  1. 繼承包含這樣一層含義:父類中凡是已經實現好的方法,實際上是在設定規範和契
    ,雖然它不強制要求所有的子類必須遵循這些契約,但是如果子類對這些已經實
    現的方法任意修改,就會對整個繼承體系造成破壞。
  2. 繼承在給程序設計帶來便利的同時,也帶來了弊端。比如使用繼承會給程序帶來
    入性
    ,程序的可移植性降低,增加對象間的耦合性
    ,如果一個類被其他的類所繼承,
    則當這個類需要修改時,必須考慮到所有的子類,並且父類修改後,所有涉及到子
    類的功能都有可能產生故障
  3. 問題提出:在編程中,如何正確的使用繼承? => 里氏替換原則
基本介紹 :
  • 如果對每個類型爲T1的對象o1,都有類型爲T2的對象o2,使得以T1定義的所有程序
    P在所有的對象o1都代換成o2時,程序P的行爲沒有發生變化,那麼類型T2是類型T1
    的子類型。換句話說,所有引用基類的地方必須能透明地使用其子類的對象。

  • 在使用繼承時,遵循里氏替換原則,在子類中儘量不要重寫父類的方法

  • 里氏替換原則告訴我們,繼承實際上讓兩個類耦合性增強了,在適當的情況下,可
    以通過聚合,組合,依賴 來解決問題。

應用實例:
package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 里氏替換原則 {
    public static void main(String[] args) {
        TestA a = new TestA();
        System.out.println("11-3=" + a.function1(11, 3));
        System.out.println("1-8=" + a.function1(1, 8));
        System.out.println("-----------------------");
        TestB b = new TestB();
        System.out.println("11-3=" + b.function1(11, 3));
        System.out.println("1-8=" + b.function1(1, 8));
        System.out.println("11+3+9=" + b.function2(11, 3));
    }
}
class TestA{
    public int function1(int a,int b){
        return a-b;
    }
}
class TestB extends TestA{
    public int function1(int a,int b){
        return a + b;
    }
    public int function2(int a,int b){
        return function1(a,b)+9;
    }
}
結果:

在這裏插入圖片描述

分析問題:
1) 我們發現原來運行正常的相減功能發生了錯誤。原因就是類B無意中重寫了父類的方法,造成原有功能出現錯誤。在實際編程中,我們常常會通過重寫父類的方法完 成新的功能,這樣寫起來雖然簡單,但整個繼承體系的複用性會比較差。特別是運 行多態比較頻繁的時候 2) 通用的做法是:原來的父類和子類都繼承一個更通俗的基類,原有的繼承關係去掉,採用依賴,聚合,組合等關係代替.
遵守里氏替換原則的代碼:
package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 里氏替換原則 {
    public static void main(String[] args) {
        TestA a = new TestA();
        System.out.println("11-3=" + a.function1(11, 3));
        System.out.println("1-8=" + a.function1(1, 8));
        System.out.println("-----------------------");
        TestB b = new TestB();
        System.out.println("11-3=" + b.function3(11, 3));
        System.out.println("1-8=" + b.function3(1, 8));
        System.out.println("11+3+9=" + b.function2(11, 3));
    }
}
class Base{
    //把更加基礎的方法和成員寫到Base類
}
class TestA extends Base{

    public int function1(int a,int b){
        return a-b;
    }
}
class TestB extends Base{
    //函數function1重寫了TestA的方法
    public int function1(int a,int b){
        return a + b;
    }
    public int function2(int a,int b){
        return function1(a,b)+9;
    }
    //我們仍然想要使用TestA的方法,使用組合關係
    private TestA a=new TestA();
    public int function3(int a,int b){
        return this.a.function1(a,b);
    }
}
結果:

在這裏插入圖片描述

3.5開閉原則

基本介紹:
應用實例:
package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 開閉原則 {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.drawShape(new Rectangle());
        editor.drawShape(new Circle());
    }
}
//基類
class Shape{
    int type;
}
//矩形
class Rectangle extends Shape{
    Rectangle(){
        super.type=1;//設置矩形的識別碼爲1
    }
}
//圓形
class Circle extends Shape{
    Circle(){
        super.type=2;//設置圓形的識別碼爲2
    }
}
//繪製圖形【使用方】
class Editor{
    //接收Shape對象,然後根據type識別碼,繪製不同圖形
    public void drawShape(Shape shape){
        if (shape.type==1){
            drawRectangle();
        }else if (shape.type==2){
            drawCircle();
        }else if (shape.type==3){

        }
    }

    private void drawRectangle() {
        System.out.println(" 繪製矩形 ");
    }

    private void drawCircle() {
        System.out.println(" 繪製圓形 ");
    }
}
分析:

方式1的優缺點

  1. 優點是比較好理解,簡單易操作。
  2. 缺點是違反了設計模式的ocp原則,即對擴展開放(提供方),對修改關閉(使用方)
    即當我們給類增加新功能的時候,儘量不修改代碼,或者儘可能少修改代碼.
  3. 比如我們這時要新增加一個圖形種類 三角形,我們需要做如下修改,修改的地方
    較多
添加繪製三角形功能代碼演示:
package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 開閉原則 {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.drawShape(new sanjiao());
    }
}
//基類
class Shape{
    int type;
}
//新添加三角形
class sanjiao extends Shape{
    sanjiao(){
        super.type=3;//設置三角形的識別碼爲3
    }
}

//繪製圖形【使用方】
class Editor{
    //接收Shape對象,然後根據type識別碼,繪製不同圖形
    public void drawShape(Shape shape){
       if (shape.type==3){
            drawSanjiao();
        }
    }


    private  void drawSanjiao(){
        System.out.println(" 繪製三角形 ");
    }
}
遵守開閉原則:

改進的思路分析
思路:把創建Shape類做成抽象類,並提供一個抽象的draw方法,讓子類去實現即可,
這樣我們有新的圖形種類時,只需要讓新的圖形類繼承Shape,並實現draw方法即可,
使用方的代碼就不需要修 -> 滿足了開閉原則

package DesignPattern.Test;

/**
 * @author 孫一鳴 on 2020/2/2
 */
public class 開閉原則 {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.drawShape(new Rectangle());
        editor.drawShape(new Circle());
        editor.drawShape(new sanjiao());
    }
}
//基類
abstract class Shape{
    int type;
    public abstract void draw();
}
//矩形
class Rectangle extends Shape{
    Rectangle(){
        super.type=1;//設置矩形的識別碼爲1
    }

    @Override
    public void draw() {
        System.out.println(" 繪製矩形 ");
    }
}
//圓形
class Circle extends Shape{
    Circle(){
        super.type=2;//設置圓形的識別碼爲2
    }
    @Override
    public void draw() {
        System.out.println(" 繪製圓形 ");
    }
}
//新添加三角形
class sanjiao extends Shape{
    sanjiao(){
        super.type=3;//設置三角形的識別碼爲3
}
    @Override
    public void draw() {
        System.out.println(" 繪製三角形 ");
    }
}

//繪製圖形【使用方】
class Editor{
    //接收Shape對象,然後根據type識別碼,繪製不同圖形
    public void drawShape(Shape shape){
      shape.draw();
    }

}

此時我們再要添加一個新的繪製圖形時,我們需要改變的只有:

//新添加其他
class Other extends Shape{
    @Override
    public void draw() {
        System.out.println(" 繪製其他圖形 ");
    }
}
 editor.drawShape(new Other());

使用方並沒有做任何代碼上的變動.滿足了OCP原則

3.6迪米特法則

基本介紹:
  1. 一個對象應該對其他對象保持最少的瞭解
  2. 類與類關係越密切,耦合度越大
  3. 迪米特法則(Demeter Principle)又叫最少知道原則,即一個類對自己依賴的類知道的
    越少越好
    。也就是說,對於被依賴的類不管多麼複雜,都儘量將邏輯封裝在類的內
    部。對外除了提供的public 方法,不對外泄露任何信息
  4. 迪米特法則還有個更簡單的定義:只與直接的朋友通信
  5. 直接的朋友:每個對象都會與其他對象有耦合關係,只要兩個對象之間有耦合關係,我們就說這兩個對象之間是朋友關係。耦合的方式很多,依賴,關聯,組合,聚合等。其中,我們稱出現成員變量,方法參數,方法返回值中的類爲直接的朋友,而出現在局部變量中的類不是直接的朋友。也就是說,陌生的類最好不要以局部變量的形式出現在類的內部。
應用實例:

有一個學校,下屬有各個學院和總部,現要求打印出學校總部員工ID和學院員工的id

package DesignPattern.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 孫一鳴 on 2020/2/3
 */


//客戶端
public class 迪米特法則 {

    public static void main(String[] args) {
        //創建了一個 SchoolManager 對象
        SchoolManager schoolManager = new SchoolManager();
        //輸出學院的員工id 和  學校總部的員工信息
        schoolManager.printAllEmployee(new CollegeManager());

    }

}


//學校總部員工類
class Employee {
    private String id;
    public void setId(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }
}
//學院的員工類
class CollegeEmployee {
    private String id;
    public void setId(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }
}


//管理學院員工的管理類
class CollegeManager {
    //返回學院的所有員工
    public List<CollegeEmployee> getAllEmployee() {
        List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
        for (int i = 0; i < 10; i++) { //這裏我們增加了10個員工到 list
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("學院員工id= " + i);
            list.add(emp);
        }
        return list;
    }
}

//學校管理類

//分析 SchoolManager 類的直接朋友類有哪些 Employee、CollegeManager
//CollegeEmployee 不是 直接朋友 而是一個陌生類,這樣違背了 迪米特法則
class SchoolManager {
    //返回學校總部的員工
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<Employee>();

        for (int i = 0; i < 5; i++) { //這裏我們增加了5個員工到 list
            Employee emp = new Employee();
            emp.setId("學校總部員工id= " + i);
            list.add(emp);
        }
        return list;
    }

    //該方法完成輸出學校總部和學院員工信息(id)
    void printAllEmployee(CollegeManager sub) {

        //分析問題
        //1. 這裏的 CollegeEmployee 不是  SchoolManager的直接朋友
        //2. CollegeEmployee 是以局部變量方式出現在 SchoolManager
        //3. 違反了 迪米特法則

        //獲取到學院員工
        List<CollegeEmployee> list1 = sub.getAllEmployee();
        System.out.println("------------學院員工------------");
        for (CollegeEmployee e : list1) {
            System.out.println(e.getId());
        }
        //獲取到學校總部員工
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("------------學校總部員工------------");
        for (Employee e : list2) {
            System.out.println(e.getId());
        }
    }
}

案例分析:
本案例共有4個實體類,分別爲:
  • 學校總部員工類CollegeEmployee
  • 學院的員工類CollegeEmployee
  • 學院員工的管理類CollegeManager
  • 學校總部員工管理類SchoolManager

出現問題:學校總部員工管理類SchoolManager 中以局部變量方式使用了 學院的員工類CollegeEmployee,違反了 迪米特法則,這裏的 CollegeEmployee 不是SchoolManager的直接朋友

案例改進:

1)前面設計的問題在於SchoolManager中,CollegeEmployee類並不是SchoolManager類的直接朋友 (分析)
2) 按照迪米特法則,應該避免類中出現這樣非直接朋友關係的耦合
3) 對代碼按照迪米特法則 進行改進

改進代碼:
package DesignPattern.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 孫一鳴 on 2020/2/3
 */


//客戶端
public class 迪米特法則 {

    public static void main(String[] args) {
        //創建了一個 SchoolManager 對象
        SchoolManager schoolManager = new SchoolManager();
        //輸出學院的員工id 和  學校總部的員工信息
        schoolManager.printAllEmployee(new CollegeManager());

    }

}


//學校總部員工類
class Employee {
    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}


//學院的員工類
class CollegeEmployee {
    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}


//管理學院員工的管理類
class CollegeManager {
    //返回學院的所有員工
    public List<CollegeEmployee> getAllEmployee() {
        List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
        for (int i = 0; i < 10; i++) { //這裏我們增加了10個員工到 list
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("學院員工id= " + i);
            list.add(emp);
        }
        return list;
    }

    public void printEmployee(){
        //獲取到學院員工
        List<CollegeEmployee> list1 = this.getAllEmployee();
        System.out.println("------------學院員工------------");
        for (CollegeEmployee e : list1) {
            System.out.println(e.getId());
        }
    }
}

//學校管理類

//分析 SchoolManager 類的直接朋友類有哪些 Employee、CollegeManager
//CollegeEmployee 不是 直接朋友 而是一個陌生類,這樣違背了 迪米特法則
class SchoolManager {
    //返回學校總部的員工
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<Employee>();

        for (int i = 0; i < 5; i++) { //這裏我們增加了5個員工到 list
            Employee emp = new Employee();
            emp.setId("學校總部員工id= " + i);
            list.add(emp);
        }
        return list;
    }

    //該方法完成輸出學校總部和學院員工信息(id)
    void printAllEmployee(CollegeManager sub) {

        //分析問題
        //1. 這裏的 CollegeEmployee 不是  SchoolManager的直接朋友
        //2. CollegeEmployee 是以局部變量方式出現在 SchoolManager
        //3. 違反了 迪米特法則

        //獲取到學院員工
        sub.printEmployee();
        //獲取到學校總部員工
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("------------學校總部員工------------");
        for (Employee e : list2) {
            System.out.println(e.getId());
        }
    }
}

迪米特法則注意事項和細節:
  1. 迪米特法則的核心是降低類之間的耦合
  2. 但是注意:由於每個類都減少了不必要的依賴,因此迪米特法則只是要求降低
    類間(對象間)耦合關係, 並不是要求完全沒有依賴關係

3.7合成複用原則

基本介紹:
原則是儘量使用合成/聚合的方式,而不是使用繼承

3.8設計原則核心思想

  1. 找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代
    碼混在一起。
  2. 針對接口編程,而不是針對實現編程。
  3. 爲了交互對象之間的鬆耦合設計而努力
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章