1. 程式人生 > 實用技巧 >jmeter(二十四)dubbo介面測試

jmeter(二十四)dubbo介面測試

設計模式的目的   編寫軟體過程中,程式設計師面臨著來自 耦合性內聚性以及可維護性可擴充套件性用性靈活性 等多方面的挑戰,設計模式是為了讓程式(軟體)更好
  • 1) 程式碼重用性 (即:相同功能的程式碼,不用多次編寫)
  • 2) 可讀性 (即:程式設計規範性, 便於其他程式設計師的閱讀和理解)
  • 3) 可擴充套件性 (即:當需要增加新的功能時,非常的方便,稱為可維護)
  • 4) 可靠性 (即:當我們增加新的功能後,對原來的功能沒有影響)
  • 5) 使程式呈現高內聚,低耦合的特性
  • 分享金句:

設計模式包含了面向物件的精髓,“懂了設計模式,你就懂了面向物件分析和設計(OOA/D)的精要”

Scott Mayers 在其鉅著《Effective C++》就曾經說過:C++老手和 C++新手的區別就是前者手背上有很多傷疤

先看幾個面試題:

  在專案實際開發中,你在哪裡使用到了ocp原則? 直譯器設計模式 1) 介紹直譯器設計模式是什麼? 2) 畫出直譯器設計模式的UML類圖,分析設計模式中的各個角色是什麼? 3) 請說明Spring的框架中,哪裡使用到了直譯器設計模式,並做原始碼級別的分析 單例設計模式一共有幾種實現方式?請分別用程式碼實現,並說明各個實現方式的優點和缺點? 單例設計模式一共有8種寫法,後面我們會依次講到 1) 餓漢式 兩種 2) 懶漢式 三種 3) 雙重檢查 4) 靜態內部類 5) 列舉 現在面試門檻越來越高,面試也不可能光背一遍概念就能面試上的,所以學好設計模式是非常有必要的。 設計模式的重要性
1) 軟體工程中,設計模式(design pattern)是對軟體設計中普遍存在(反覆出現)的各種問題,所提出的解決方案。這個術語是由埃裡希·伽瑪(Erich Gamma)等人在1990年代從建築設計領域引入到電腦科學的 2) 大廈 VS 簡易房 3) 拿實際工作經歷來說, 當一個專案開發完後,如果客戶提出增新功能,怎麼辦?。 4) 如果專案開發完後,原來程式設計師離職,你接手維護該專案怎麼辦? (維護性[可讀性、規範性]) 5) 目前程式設計師門檻越來越高,一線IT公司(大廠),都會問你在實際專案中使用過什麼設計模式,怎樣使用的,解決了什麼問題。 6) 設計模式在軟體中哪裡?面向物件(oo)=>功能模組[設計模式+演算法(資料結構)]=>框架[使用到多種設計模式]=>架構 [伺服器叢集] 7) 如果想成為合格軟體工程師,那就花時間來研究下設計模式是非常必要的. 設計模式七大原則
設計模式都是根據這7種原則構造出來的。 設計模式原則,其實就是程式設計師在程式設計時,應當遵守的原則,也是各種設計模式的基礎(即:設計模式為什麼這樣設計的依據) 設計模式常用的七大原則有: 1) 單一職責原則 2) 介面隔離原則 3) 依賴倒轉(倒置)原則 4) 里氏替換原則 5) 開閉原則 6) 迪米特法則 7) 合成複用原則 (重要)

單一職責原則

基本介紹   對類來說的,即一個類應該只負責一項職責(別誤解為一個類只寫一個方法)。如類A負責兩個不同職責:職責1,職責2。當職責1需求變更而改變A時,可能造成職責2執行錯誤,所以需要將類A的粒度分解為A1,A2   意思就是,比如dao層,有order訂單,user客戶,兩個不同的dao,是可以寫在一個dao裡定義的,但這樣不好,太雜亂,所以要分為兩個dao,意思就是細化。 單一職責原則注意事項和細節 1) 降低類的複雜度,一個類只負責一項職責。 2) 提高類的可讀性,可維護性 3) 降低變更引起的風險 4) 通常情況下,我們應當遵守單一職責原則,只有邏輯足夠簡單,才可以在程式碼級違反單一職責原則;只有類中方法數量足夠少,可以在方法級別保持單一職責原則 示例: 錯誤程式碼:
public class SingleResponsibility1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托車");
        vehicle.run("汽車");
        vehicle.run("飛機");
    }

}

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

問題:最後會列印,飛機也在公路上執行???

解決方案:

解決方案有很多種,比如多寫幾個類:

public class SingleResponsibility2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        RoadVehicle roadVehicle = new RoadVehicle();
        roadVehicle.run("摩托車");
        roadVehicle.run("汽車");
        
        AirVehicle airVehicle = new AirVehicle();
        
        airVehicle.run("飛機");
    }

}

//方案2的分析
//1. 遵守單一職責原則
//2. 但是這樣做的改動很大,即將類分解,同時修改客戶端
//3. 改進:直接修改Vehicle 類,改動的程式碼會比較少=>方案3

class RoadVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "公路執行");
    }
}

class AirVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "天空執行");
    }
}

class WaterVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "水中執行");
    }
}

這樣雖然解決了違反單一職責原則的問題,但是這樣不好,原因在類中已經標出。

改進方法

public class SingleResponsibility3 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Vehicle2 vehicle2  = new Vehicle2();
        vehicle2.run("汽車");
        vehicle2.runWater("輪船");
        vehicle2.runAir("飛機");
    }

}

//方式3的分析
//1. 這種修改方法沒有對原來的類做大的修改,只是增加方法
//2. 這裡雖然沒有在類這個級別上遵守單一職責原則,但是在方法級別上,仍然是遵守單一職責
class Vehicle2 {
    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 + " 在水中行....");
    }
    
    //方法2.  方法多的話,還是要寫多個類,保證單一,細化每個類的任務
    //..
    //..
    
    //...
}

這樣算比較好的了,但是也沒有完全遵守單一職責原則,

單一職責原則可違反的條件是:方法足夠少的情況下,可以適當違法,如果業務非常多,方法也會寫很多,肯定要寫多個類分開寫方法,保證單一職責。

只有邏輯足夠簡單,才可以在程式碼級違反單一職責原則;只有類中方法數量足夠少,可以在方法級別保持單一職責原則

介面隔離原則

基本介紹   客戶端不應該依賴一個不需要的介面,即一個類對另一個類的依賴應該建立在最小的介面上;   意思就是,一個介面類裡有多個介面,但是有的類要用的只是裡面一個介面,可以實現了這個介面的類,就要重寫裡面的所有介面 按隔離原則應當這樣處理 將介面Interface拆分為獨立的幾個介面,類1和類2分別與他們需要的介面建立依賴關係。也就是採用介面隔離原則 示例
public class Segregation1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

}

//介面
interface Interface1 {
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}

class B implements Interface1 {
    public void operation1() {
        System.out.println("B 實現了 operation1");
    }
    
    public void operation2() {
        System.out.println("B 實現了 operation2");
    }
    public void operation3() {
        System.out.println("B 實現了 operation3");
    }
    public void operation4() {
        System.out.println("B 實現了 operation4");
    }
    public void operation5() {
        System.out.println("B 實現了 operation5");
    }
}

class D implements Interface1 {
    public void operation1() {
        System.out.println("D 實現了 operation1");
    }
    
    public void operation2() {
        System.out.println("D 實現了 operation2");
    }
    public void operation3() {
        System.out.println("D 實現了 operation3");
    }
    public void operation4() {
        System.out.println("D 實現了 operation4");
    }
    public void operation5() {
        System.out.println("D 實現了 operation5");
    }
}

class A { //A 類通過介面Interface1 依賴(使用) B類,但是隻會用到1,2,3方法
    public void depend1(Interface1 i) {
        i.operation1();
    }
    public void depend2(Interface1 i) {
        i.operation2();
    }
    public void depend3(Interface1 i) {
        i.operation3();
    }
}
  
class C { //C 類通過介面Interface1 依賴(使用) 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();
    }
}

這段程式碼是通過上面類圖編寫的,這樣不好,現在要把這些介面進行拆分。

拆分後的:

按照原來的關聯需求,拆分成這樣的

程式碼:

public class Segregation1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 使用一把
        A a = new A();
        a.depend1(new B()); // A類通過介面去依賴B類
        a.depend2(new B());
        a.depend3(new B());

        C c = new C();

        c.depend1(new D()); // C類通過介面去依賴(使用)D類
        c.depend4(new D());
        c.depend5(new D());

    }

}

// 介面1
interface Interface1 {
    void operation1();

}

// 介面2
interface Interface2 {
    void operation2();

    void operation3();
}

// 介面3
interface Interface3 {
    void operation4();

    void operation5();
}

class B implements Interface1, Interface2 {
    public void operation1() {
        System.out.println("B 實現了 operation1");
    }

    public void operation2() {
        System.out.println("B 實現了 operation2");
    }

    public void operation3() {
        System.out.println("B 實現了 operation3");
    }

}

class D implements Interface1, Interface3 {
    public void operation1() {
        System.out.println("D 實現了 operation1");
    }

    public void operation4() {
        System.out.println("D 實現了 operation4");
    }

    public void operation5() {
        System.out.println("D 實現了 operation5");
    }
}

class A { // A 類通過介面Interface1,Interface2 依賴(使用) B類,但是隻會用到1,2,3方法
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend2(Interface2 i) {
        i.operation2();
    }

    public void depend3(Interface2 i) {
        i.operation3();
    }
}

class C { // C 類通過介面Interface1,Interface3 依賴(使用) D類,但是隻會用到1,4,5方法
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend4(Interface3 i) {
        i.operation4();
    }

    public void depend5(Interface3 i) {
        i.operation5();
    }
}

這樣把介面都給拆成需要的樣子,避免實現不需要的介面,就叫介面隔離。

依賴倒轉原則

基本介紹 1) 高層模組不應該依賴低層模組,二者都應該依賴其抽象 2) 抽象不應該依賴細節,細節應該依賴抽象 3) 依賴倒轉(倒置)的中心思想是面向介面程式設計 4) 依賴倒轉原則是基於這樣的設計理念:相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建的架構比以細節為基礎的架構要穩定的多。在java中,抽象指的是介面或抽象類,細節就是具體的實現類 5) 使用介面或抽象類的目的是制定好規範,而不涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成 依賴倒轉原則的注意事項和細節 1) 低層模組儘量都要有抽象類或介面,或者兩者都有,程式穩定性更好. 2) 變數的宣告型別儘量是抽象類或介面, 這樣我們的變數引用和實際物件間,就存在一個緩衝層,利於程式擴充套件和優化 3) 繼承時遵循里氏替換原則 例項 1) 請程式設計完成Person 接收訊息 的功能。 2) 實現方案1 + 分析說明 3) 實現方案2 + 分析說明
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. 簡單,比較容易想到
//2. 如果我們獲取的物件是 微信,簡訊等等,則新增類,同時Perons也要增加相應的接收方法
//3. 解決思路:引入一個抽象的介面IReceiver, 表示接收者, 這樣Person類與介面IReceiver發生依賴
//   因為Email, WeiXin 等等屬於接收的範圍,他們各自實現IReceiver 介面就ok, 這樣我們就符號依賴倒轉原則
class Person {
    public void receive(Email email ) {
        System.out.println(email.getInfo());
    }
}

解決思路:

public class DependecyInversion {

    public static void main(String[] args) {
        //客戶端無需改變
        Person person = new Person();
        person.receive(new Email());
        
        person.receive(new WeiXin());
    }

}

//定義介面
interface IReceiver {
    public String getInfo();
}

class Email implements IReceiver {
    public String getInfo() {
        return "電子郵件資訊: hello,world";
    }
}

//增加微信
class WeiXin implements IReceiver {
    public String getInfo() {
        return "微信資訊: hello,ok";
    }
}

//方式2
class Person {
    //這裡我們是對介面的依賴
    public void receive(IReceiver receiver ) {
        System.out.println(receiver.getInfo());
    }
}
依賴關係傳遞的三種方式和應用案例 1) 介面傳遞 2) 構造方法傳遞 3) setter方式傳遞

程式碼:

public class DependencyPass {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ChangHong changHong = new ChangHong();
//        OpenAndClose openAndClose = new OpenAndClose();
//        openAndClose.open(changHong);
        
        //通過構造器進行依賴傳遞
//        OpenAndClose openAndClose = new OpenAndClose(changHong);
//        openAndClose.open();
        //通過setter方法進行依賴傳遞
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.setTv(changHong);
        openAndClose.open();

    }

}

// 方式1: 通過介面傳遞實現依賴
// 開關的介面
// interface IOpenAndClose {
// public void open(ITV tv); //抽象方法,接收介面
// }
//
// interface ITV { //ITV介面
// public void play();
// }
// 
// class ChangHong implements ITV {
//
//    @Override
//    public void play() {
//        // TODO Auto-generated method stub
//        System.out.println("長虹電視機,開啟");
//    }
//     
// }
//// 實現介面
// class OpenAndClose implements IOpenAndClose{
// public void open(ITV tv){
// tv.play();
// }
// }

// 方式2: 通過構造方法依賴傳遞
// interface IOpenAndClose {
// public void open(); //抽象方法
// }
// interface ITV { //ITV介面
// public void play();
// }
// class OpenAndClose implements IOpenAndClose{
// public ITV tv; //成員
// public OpenAndClose(ITV tv){ //構造器
// this.tv = tv;
// }
// public void open(){
// this.tv.play();
// }
// }


// 方式3 , 通過setter方法傳遞
interface IOpenAndClose {
    public void open(); // 抽象方法

    public void setTv(ITV tv);
}

interface ITV { // ITV介面
    public void play();
}

class OpenAndClose implements IOpenAndClose {
    private ITV tv;

    public void setTv(ITV tv) {
        this.tv = tv;
    }

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

class ChangHong implements ITV {

    @Override
    public void play() {
        // TODO Auto-generated method stub
        System.out.println("長虹電視機,開啟");
    }
     
}

這三種方式在開發中都是經常使用的。

里氏替換原則

繼承性的思考和說明 1) 繼承包含這樣一層含義:父類中凡是已經實現好的方法,實際上是在設定規範和契約,雖然它不強制要求所有的子類必須遵循這些契約,但是如果子類對這些已經實現的方法任意修改,就會對整個繼承體系造成破壞。 2) 繼承在給程式設計帶來便利的同時,也帶來了弊端。比如使用繼承會給程式帶來侵入性,程式的可移植性降低,增加物件間的耦合性,如果一個類被其他的類所繼承,則當這個類需要修改時,必須考慮到所有的子類,並且父類修改後,所有涉及到子類的功能都有可能產生故障 3) 問題提出:在程式設計中,如何正確的使用繼承? => 里氏替換原則 基本介紹 1) 里氏替換原則(Liskov Substitution Principle)在1988年,由麻省理工學院的以為姓裡的女士提出的。 2) 如果對每個型別為T1的物件o1,都有型別為T2的物件o2,使得以T1定義的所有程式P在所有的物件o1都代換成o2時,程式P的行為沒有發生變化,那麼型別T2是型別T1的子型別。換句話說,所有引用基類的地方必須能透明地使用其子類的物件。 3) 在使用繼承時,遵循里氏替換原則,在子類中儘量不要重寫父類的方法 4) 里氏替換原則告訴我們,繼承實際上讓兩個類耦合性增強了,在適當的情況下,可以通過聚合,組合,依賴 來解決問題。 示例
public class Liskov {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        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();
        System.out.println("11-3=" + b.func1(11, 3));//這裡本意是求出11-3
        System.out.println("1-8=" + b.func1(1, 8));// 1-8
        System.out.println("11+3+9=" + b.func2(11, 3));
        
        

    }

}

// A類
class A {
    // 返回兩個數的差
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}

// B類繼承了A
// 增加了一個新功能:完成兩個數相加,然後和9求和
class B extends A {
    //這裡,重寫了A類的方法, 可能是無意識
    public int func1(int a, int b) {
        return a + b;
    }

    public int func2(int a, int b) {
        return func1(a, b) + 9;
    }
}
解決方法 1) 我們發現原來執行正常的相減功能發生了錯誤。原因就是類B無意中重寫了父類的方法,造成原有功能出現錯誤。在實際程式設計中,我們常常會通過重寫父類的方法完成新的功能,這樣寫起來雖然簡單,但整個繼承體系的複用性會比較差。特別是執行多型比較頻繁的時候 2) 通用的做法是:原來的父類和子類都繼承一個更通俗的基類,原有的繼承關係去掉,採用依賴聚合組合等關係代替
public class Liskov {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        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();
        //因為B類不再繼承A類,因此呼叫者,不會再func1是求減法
        //呼叫完成的功能就會很明確
        System.out.println("11+3=" + b.func1(11, 3));//這裡本意是求出11+3
        System.out.println("1+8=" + b.func1(1, 8));// 1+8
        System.out.println("11+3+9=" + b.func2(11, 3));
        
        
        //使用組合仍然可以使用到A類相關方法
        System.out.println("11-3=" + b.func3(11, 3));// 這裡本意是求出11-3
        

    }

}

//建立一個更加基礎的基類
class Base {
    //把更加基礎的方法和成員寫到Base類
}

// A類
class A extends Base {
    // 返回兩個數的差
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}

// B類繼承了A
// 增加了一個新功能:完成兩個數相加,然後和9求和
class B extends Base {
    //如果B需要使用A類的方法,使用組合關係
    private A a = new A();
    
    //這裡,重寫了A類的方法, 可能是無意識
    public int func1(int a, int b) {
        return a + b;
    }

    public int func2(int a, int b) {
        return func1(a, b) + 9;
    }
    
    //我們仍然想使用A的方法
    public int func3(int a, int b) {
        return this.a.func1(a, b);
    }
}

開閉原則

基本介紹 1) 開閉原則(Open Closed Principle)是程式設計中最基礎、最重要的設計原則 2) 一個軟體實體如類,模組和函式應該對擴充套件開放(對提供方),對修改關閉(對使用方)。用抽象構建框架,用實現擴充套件細節。 3) 當軟體需要變化時,儘量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有的程式碼來實現變化。 4) 程式設計中遵循其它原則,以及使用設計模式的目的就是遵循開閉原則。 思路分析 思路:把建立類做成抽象類,並提供一個抽象的方法,讓子類去實現即可,這樣我們有新的圖形種類時,只需要讓新的圖形類繼承抽象類,並實現抽象方法即可,使用方的程式碼就不需要修 -> 滿足了開閉原則

示例:對原來的程式碼新增三角形打印出來

public class Ocp {

    public static void main(String[] args) {
        //使用看看存在的問題
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
    }

}

//這是一個用於繪圖的類 [使用方]
class GraphicEditor {
    //接收Shape物件,然後根據type,來繪製不同的圖形
    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);
    }

    //繪製矩形
    public void drawRectangle(Shape r) {
        System.out.println(" 繪製矩形 ");
    }

    //繪製圓形
    public void drawCircle(Shape r) {
        System.out.println(" 繪製圓形 ");
    }
    
    //繪製三角形
    public void drawTriangle(Shape r) {
        System.out.println(" 繪製三角形 ");
    }
}

//Shape類,基類
class Shape {
    int m_type;
}

class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }
}

class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }
}

//新增畫三角形
class Triangle extends Shape {
    Triangle() {
        super.m_type = 3;
    }
}
這段程式碼優缺點 1) 優點是比較好理解,簡單易操作。 2) 缺點是違反了設計模式的ocp原則,即對擴充套件開放(提供方),對修改關閉(使用方)。即當我們給類增加新功能的時候,儘量不修改程式碼,或者儘可能少修改程式碼. 3) 比如我們這時要新增加一個圖形種類 三角形,我們需要做如下修改,修改的地方較多 改進的思路分析 思路:把建立Shape類做成抽象類,並提供一個抽象的draw方法,讓子類去實現即可,這樣我們有新的圖形種類時,只需要讓新的圖形類繼承Shape,並實現draw方法即可,使用方的程式碼就不需要修 -> 滿足了開閉原則
public class Ocp {

    public static void main(String[] args) {
        //使用看看存在的問題
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
        graphicEditor.drawShape(new OtherGraphic());
    }

}

//這是一個用於繪圖的類 [使用方]
class GraphicEditor {
    //接收Shape物件,呼叫draw方法
    public void drawShape(Shape s) {
        s.draw();
    }

    
}

//Shape類,基類
abstract class Shape {
    int m_type;
    
    public abstract void draw();//抽象方法
}

class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }

    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 繪製矩形 ");
    }
}

class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }
    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 繪製圓形 ");
    }
}

//新增畫三角形
class Triangle extends Shape {
    Triangle() {
        super.m_type = 3;
    }
    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 繪製三角形 ");
    }
}

//新增一個圖形
class OtherGraphic extends Shape {
    OtherGraphic() {
        super.m_type = 4;
    }

    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 繪製其它圖形 ");
    }
}

迪米特法則

基本介紹 1) 一個物件應該對其他物件保持最少的瞭解 2) 類與類關係越密切,耦合度越大 3) 迪米特法則(Demeter Principle)又叫最少知道原則,即一個類對自己依賴的類知道的越少越好。也就是說,對於被依賴的類不管多麼複雜,都儘量將邏輯封裝在類的內部。對外除了提供的public 方法,不對外洩露任何資訊 4) 迪米特法則還有個更簡單的定義:只與直接的朋友通訊 5) 直接的朋友:每個物件都會與其他物件有耦合關係,只要兩個物件之間有耦合關係,我們就說這兩個物件之間是朋友關係。耦合的方式很多,依賴,關聯,組合,聚合等。其中,我們稱出現成員變數,方法引數,方法返回值中的類為直接的朋友,而出現在區域性變數中的類不是直接的朋友。也就是說,陌生的類最好不要以區域性變數的形式出現在類的內部迪米特法則注意事項和細節 1) 迪米特法則的核心是降低類之間的耦合 2) 但是注意:由於每個類都減少了不必要的依賴,因此迪米特法則只是要求降低類間(物件間)耦合關係, 並不是要求完全沒有依賴關係 例項 1) 有一個學校,下屬有各個學院和總部,現要求打印出學校總部員工ID和學院員工的id 2) 程式設計實現上面的功能,
import java.util.ArrayList;
import java.util.List;

//客戶端
public class Demeter1 {

    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());
        }
    }
}
應用例項改進 1) 前面設計的問題在於SchoolManager中,CollegeEmployee類並不是SchoolManager類的直接朋友 (分析) 2) 按照迪米特法則,應該避免類中出現這樣非直接朋友關係的耦合 3) 對程式碼按照迪米特法則 進行改進
import java.util.ArrayList;
import java.util.List;

//客戶端
public class Demeter1 {

    public static void main(String[] args) {
        System.out.println("~~~使用迪米特法則的改進~~~");
        //建立了一個 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 = 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. 將輸出學院的員工方法,封裝到CollegeManager
        sub.printEmployee();
    
        //獲取到學校總部員工
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("------------學校總部員工------------");
        for (Employee e : list2) {
            System.out.println(e.getId());
        }
    }
}

合成複用原則

  基本介紹

    原則是儘量使用合成/聚合的方式,而不是使用繼承,看下面UML類圖

比如:

這樣A和B的耦合性非常強,B要用A的方法,就會有A的所有方法

改進:

改進的方式有多種,比如依賴關係:

或者聚合:

或者組合方式:

UML類圖

UML基本介紹 1) UML——Unified modeling language UML(統一建模語言),是一種用於軟體系統分析和設計的語言工具,它用於幫助軟體開發人員進行思考和記錄思路的結果 2) UML本身是一套符號的規定,就像數學符號和化學符號一樣,這些符號用於描述軟體模型中的各個元素和他們之間的關係,比如類、介面、實現、泛化、依賴、組合、聚合等,如右圖: 3) 使用UML來建模,常用的工具有 RationalRose , 也可以使用一些外掛來建模 畫UML圖與寫文章差不多,都是把自己的思想描述給別人看,關鍵在於思路和條理, UML圖分類: 1) 用例圖(最重要) 2) 靜態結構圖:類圖、物件圖、包圖、元件圖、部署圖 3) 動態行為圖:互動圖(時序圖與協作圖)、狀態圖、活動圖 說明: 1) 類圖是描述類與類之間的關係的,是UML圖中最核心的 2) 在講解設計模式時,我們必然會使用類圖,為了能夠把設計模式學到位,需要先講解類圖 類圖是什麼? 用於描述系統中的類(物件)本身的組成和類(物件)之間的各種靜態關係。 類之間的關係:依賴、泛化(繼承)、實現、關聯、聚合與組合 依賴關係(Dependence) 只要是在類中用到了對方,那麼他們之間就存在依賴關係。如果沒有對方,連編繹都通過不了 以下5種都是可以構成依賴關係 1) 類中用到了對方 2) 如果是類的成員屬性 3) 如果是方法的返回型別 4) 是方法接收的引數型別 5) 方法中使用到    泛化關係(generalization)   泛化關係實際上就是繼承關係,他是依賴關係的特例 1) 泛化關係實際上就是繼承關係 2) 如果A類繼承了B類,我們就說A和B存在泛化關係
實現關係(Implementation)   實現關係實際上就是A類實現B介面,他是依賴關係的特例 關聯關係(Association) 關聯關係實際上就是類與類之間的聯絡,他是依賴關係的特例 關聯具有導航性:即雙向關係或單向關係 單向一對一關係 雙向一對一關係    聚合關係(Aggregation) 聚合關係(Aggregation)表示的是整體和部分的關係,整體與部分可以分開。聚合關係是關聯關係的特例,所以他具有關聯的導航性與多重性。 如:一臺電腦由鍵盤(keyboard)、顯示器(monitor),滑鼠等組成;組成電腦的各個配件是可以從電腦上分離出來的,使用帶空心菱形的實線來表示,如果我們人Mouse,Monitor和Computer是不可分離的,則升級為組合關係    組合關係(Composition) 組合關係:也是整體與部分的關係,但是整體與部分不可以分開。再看一個案例:在程式中我們定義實體:人與身份證、頭, 那麼 頭和人就是 組合,身份證和 人就是聚合。 但是如果在程式中Person實體中定義了對IDCard進行級聯刪除,即刪除Person時連同IDCard一起刪除,那麼IDCard 和 Person 就是組合了.    掌握設計模式的層次 1) 第1層:剛開始學程式設計不久,聽說過什麼是設計模式 2) 第2層:有很長時間的程式設計經驗,自己寫了很多程式碼,其中用到了設計模式,但是自己卻不知道 3) 第3層:學習過了設計模式,發現自己已經在使用了,並且發現了一些新的模式挺好用的 4) 第4層:閱讀了很多別人寫的原始碼和框架,在其中看到別人設計模式,並且能夠領會設計模式的精妙和帶來的好處。 5) 第5層:程式碼寫著寫著,自己都沒有意識到使用了設計模式,並且熟練的寫了出來。 設計模式介紹 1) 設計模式是程式設計師在面對同類軟體工程設計問題所總結出來的有用的經驗,模式不是程式碼,而是某類問題的通用解決方案,設計模式(Design pattern)代表了最佳的實踐。這些解決方案是眾多軟體開發人員經過相當長的一段時間的試驗和錯誤總結出來的。 2) 設計模式的本質提高 軟體的維護性,通用性和擴充套件性,並降低軟體的複雜度。 3) <<設計模式>> 是經典的書,作者是 Erich Gamma、Richard Helm、RalphJohnson 和 John Vlissides Design(俗稱 “四人組 GOF”) 4) 設計模式並不侷限於某種語言,java,php,c++ 都有設計模式. 設計模式分為三種類型,共23種 1) 建立型模式:單例模式、抽象工廠模式、原型模式、建造者模式、工廠模式。 2) 結構型模式:介面卡模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。 3) 行為型模式:模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、直譯器模式(Interpreter模式)、狀態模式、策略模式、職責鏈模式(責任鏈模式)。