1. 程式人生 > >設計模式---設計原則

設計模式---設計原則

  • 介紹

什麼是設計模式:就是一種大家都經常使用的一種解決方案,是一套被反覆使用、多數人知曉的、經過分類的、程式碼設計經驗的總結。設計模式不區分語言,只要是面向物件程式中都可以使用。

通常被有經驗的面向物件的軟體開發人員所採用。設計模式是軟體開發人員在軟體開發過程中面臨的一般問題的解決方案。這些解決方案是眾多軟體開發人員經過相當長的一段時間的試驗和錯誤總結出來的。

為什麼要使用設計模式:可以提高程式碼可讀性、可擴充套件性,讓程式碼更容易被他人理解、保證程式碼可靠性。

設計原則

Single Responsibility Principle : 單一職責原則

Liskov Substitution Principle     : 里氏替換原則

Dependence Inversion Principle :依賴倒置原則

Interface Segregation Principle  : 介面隔離原則

Law of Demeter          : 迪米特法則

Open Closed Principle             : 開閉原則

面向物件設計原則為支援可維護性複用而誕生,這些原則蘊含在很多設計模式中,它們是從許多設計方案中總結出的指導性原則設計模式根據這結設計原則進行設計總結。

軟體開發之所以會有這些原則,就是因為複雜多變且不可預料的需求。並不是說在實際專案開發中對這六大原則中的每一條都遵循到極致,而是說在專案開發的過程中,根據專案的實際需求儘量的去遵守這些原則。當然要做到這些肯定是不容易的,能真正做到並且做好的恐怕也只能是有經驗之人。

1、開閉原則(Open Close Principle)

開閉原則的意思是:對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。簡言之,是為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。

2、里氏代換原則(Liskov Substitution Principle)

里氏代換原則是面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

3、依賴倒轉原則(Dependence Inversion Principle)

這個原則是開閉原則的基礎,具體內容:針對介面程式設計,依賴於抽象而不依賴於具體。

4、介面隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟體架構出發、便於升級和維護的軟體設計思想,它強調降低依賴,降低耦合。

5、迪米特法則,又稱最少知道原則(Demeter Principle)

最少知道原則是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。

6、合成複用原則(Composite Reuse Principle)

合成複用原則是指:儘量使用合成/聚合的方式,而不是使用繼承。

單一職責原則

應該有且只有一個原因引起類的變更。換句話說就是一個介面只做一件事,即一個職責一個介面。但是困難的是劃分職責時並沒有一個標準,最終都是需要從實際的專案去考慮。我們在設計的時候,儘量單一,然後對於其實現類就要多方面的考慮。不能死套單一職責原則,否則會增加很多類,給維護帶來不便。

一個類只負責一個功能領域中的相應職責,就一個類而言,類的設計儘量做到只有一個原因可以引起它的改變。

一個User類圖,目前有屬性與使用方法:明顯不符合單一原則。

通過改造成後,把具體業務放到service層,建立user 操作物件

總結:介面一定做到單一職責,類的設計儘量做到只有一個原因可以引起它的改變。

里氏替換原則

里氏替換原則簡單易懂一點的定義就是隻要父類出現的地方子類就可以出現,且替換成子類也不會出現任何錯誤或者異常。(但是反過來,有子類出現的地方,父類不一定可以適用)。

在軟體中將一個基類物件替換成它的子類物件,程式將不會產生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類物件的話,那麼它不一定能夠使用基類物件。例如:我喜歡動物,那我一定喜歡狗,因為狗是動物的子類;但是我喜歡狗,不能據此斷定我喜歡動物,因為我並不喜歡老鼠,雖然它也是動物。

abstract class AbstractGun{

    public abstract void shoot();    

}

class Handgun extends AbstractGun {

    @Override

    public void shoot() {

        System.out.println("Handgun shoot ......");        

    }

}

class Machinegun extends AbstractGun {

    @Override

    public void shoot() {

        System.out.println("Machinegun shoot ......");        

    }

}

class Solider{

    private AbstractGun gun;
//此處的AbstractGun gun 是父類出現的地方,子類可以出現 體現在傳給它的實際引數型別可以是Machinegun或者Handgun任一種型別,並且都不會出錯

    public void setGun(AbstractGun gun){

        this.gun = gun;

    }

    public void kill(){

        this.gun.shoot();

    }

}public class Client{

    public static void main(String[] args){

        Solider solider = new Solider();

        //傳入的時候並不知道是哪種型別 ,執行時才知道,而且修改槍支的型別只需要new 不同的物件即可。而不用修改其他的任何地方

        solider.setGun(new Machinegun());

        solider.kill();

    }

}

依賴倒置原則

所謂依賴倒置原則,就是不論高層元件和低層元件都應該依賴於抽象,而不是具體實現類。聽起來更像是“針對介面程式設計,而不是針對實現程式設計”,但是這裡依賴倒置原則更強調“抽象”的概念,不要讓高層元件依賴低層元件,更不能依賴具體實現類,都要依賴於抽象。依賴倒置原則的核心在於“面向介面程式設計”,目的在於”解耦“

模組間的依賴關係通過介面和抽象類產生,實體類之間不直接發生依賴關係

在實現依賴倒轉原則時,我們需要針對抽象層程式設計,而將具體類的物件通過依賴注入(DependencyInjection, DI)的方式注入到其他物件中,依賴注入是指當一個物件要與其他物件發生依賴關係時,通過抽象來注入所依賴的物件。常用的注入方式有三種,分別是:構造注入,設值注入(Setter注入)和介面注入。

interface Car{

    public void run();

}

class Benz implements Car{

    public void run(){

        System.out.println("benz run......");

    }

}class BMW implements Car{

    public void run(){

        System.out.println("bmw run......");

    }

}

class Driver{

       //讓Driver類依賴一個Car這個介面

    public void drive(Car car){

        car.run();

    }

}
public class Client{

    public static void main(String[] args){

        Driver driver = new Driver();

        driver.drive(new BMW());

    }

}

介面隔離原則

  1. 定義: 建立單一介面,不要建立臃腫龐大的介面。即介面儘量細化,同時介面中的方法儘量少。在這裡提一下單一職責和介面隔離原則的區別。首先兩個側重點是不一樣的.單一職責要求類和介面,或者方法的職責單一,側重點在職責,這是根據業務邏輯進行劃分的。而介面隔離原則要介面中的方法儘量少。比如,一個介面或者一箇中有十個方法,不同的方法做不同的事情.
  2. 使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。

 介面隔離原則是對介面定義的規範。含義主要包含以下4點。

①介面儘量小。根據具體業務把一個介面按照介面隔離原則細化成更多的介面。但是在此基礎之上必須不能違背單一職責原則。

②介面要高內聚。高內聚的意思就是提高介面和類處理能力,減少對外的互動。介面是對外的承諾,因此設計時應該儘量少公佈介面中的public方法,承諾越少系統開發越有利且變更風險就越少。

③定製服務。定製服務就是單獨為一個個體提供服務,即只提供訪問者需要的方法。舉一個圖書管理系統的例子,有一個查詢介面BookSearch,包括如下方法:searchById,searchByBookName,searchByCategory,complexSearch,其中前三個方法是提供給學生使用的,後一個方法是提供給管理員使用的,學生對這個方法的訪問是有限制的,呼叫不會返回任何值。當這四個方法全部公佈出去之後,學生對此方法的訪問即使不返回任何值也會使伺服器效能下降。因此合理的設計應該是拆分這個介面為兩個介面:SimpleSearch和AdminSearch。SimpleSearch介面提供searchById,searchByBookName,searchByCategory方法,AdminSearch介面提供complexSearch方法,此時學生實現SimpleSearch介面即可,管理員同時實現SimpleSearch和AdminSearch兩個介面。

④介面設計是有限度的。介面設計越小越好,但是結構同時會變得複雜,維護也變得難了。因此就要把握住這個度。

迪米特法則

一個軟體實體應當儘可能少地與其他實體發生相互作用

1. 定義: 迪米特法則也叫做最少知識原則(Least Knowledge Principle,LKP),即一個物件應該對其他物件有最少的瞭解,也就是說一個類要對自己需要耦合或者呼叫的類知道的最少。我只知道你有多少public方法可以供我呼叫,而其他的一切都與我無關。

2. 迪米特法則是對類的低耦合做處理明確的要求,在此舉一個例子:學校領導老師點名,老師讓體育委員清點人數。其中第二段程式碼的耦合性較第一段程式碼有所改善。

迪米特法則的核心觀念就是類間解耦,最終可能產生的結果就是會產生了大量的中轉類。為了把解耦做到極致導致實現一個業務邏輯的實現跳轉了很多類,這也是不可取的做法。因此根據實際權衡利弊才是重要的。

開閉原則

一個軟體實體應當對擴充套件開放,對修改關閉。即軟體實體應儘量在不修改原有程式碼的情況下進行擴充套件

  1. 定義:開閉原則是Java裡最基礎的設計原則。具體的定義是:一個軟體實體,比如類,模組,函式應該對擴充套件開放,對修改關閉。說的通熟易懂一些就是一個軟體實體應該通過擴充套件來實現變化,而不是通過修改已有的程式碼來實現改變。
  2. 不要修改原始碼,設計出可以擴充套件方式。

設計模式分類:

設計模式分為三種類型,共23種:

建立型模式單例模式、抽象工廠模式、建造者模式、工廠模式、原型模式。

結構型模式介面卡模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。

行為型模式模版方法模式、命令模式、迭代器模式觀察者模式、中介者模式、備忘錄模式、直譯器模式(Interpreter模式)、狀態模式、策略模式、職責鏈模式(責任鏈模式)、訪問者模式。