1. 程式人生 > >面向對象的三大特性與五大基本原則

面向對象的三大特性與五大基本原則

同時 屬性 open for idt emma 這樣的 interface bili 體系

三大特性:封裝,繼承,多態

封裝:就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。封裝是面向對象的特征之一,是對象和類概念的主要特性。 簡單的說,一個類就是一個封裝了數據以及操作這些數據的代碼的邏輯實體。在一個對象內部,某些代碼或某些數據可以是私有的,不能被外界訪問。通過這種方式,對象對內部數據提供了不同級別的保護,以防止程序中無關的部分意外的改變或錯誤的使用了對象的私有部分。

繼承:是指可以讓某個類型的對象獲得另一個類型的對象的屬性的方法。它支持按級分類的概念。繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。 通過繼承創建的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”、“父類”或“超類”。繼承的過程,就是從一般到特殊的過程。要實現繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實現。繼承概念的實現方式有二類:實現繼承與接口繼承。實現繼承是指直接使用基類的屬性和方法而無需額外編碼的能力;接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力。

多態:是指一個類實例的相同方法在不同情形有不同表現形式。多態機制使具有不同內部結構的對象可以共享相同的外部接口。這意味著,雖然針對不同對象的具體操作不同,但通過一個公共的類,它們(那些操作)可以通過相同的方式予以調用。

五大基本原則

單一職責原則、開放封閉原則、接口隔離原則、裏氏替換原則和依賴倒置原則

單一職責原則(SRP:Single responsibility principle)又稱單一功能原則

  對於單一職責原則,其核心思想為:一個類,最好只做一件事,只有一個引起它的變化。單一職責原則可以看做是低耦合、高內聚在面向對象原則上的引申,將職責定義為引起變化的原因,以提高內聚性來減少引起變化的原因。職責過多,可能引起它變化的原因就越多,這將導致職責依賴,相互之間就產生影響,從而大大損傷其內聚性和耦合度。通常意義下的單一職責,就是指只有一種單一功能,不要為類實現過多的功能點,以保證實體只有一個引起它變化的原因。

 專註,是一個人優良的品質;同樣的,單一也是一個類的優良設計。交雜不清的職責將使得代碼看起來特別別扭牽一發而動全身,有失美感和必然導致醜陋的系統錯誤風險。

開放封閉原則(OPC:Open Close Principe)

對於開放封閉原則,它是面向對象所有原則的核心,軟件設計說到底追求的目標就是封裝變化、降低耦合,而開放封閉原則就是這一目標的最直接體現。 遵循開閉原則設計出的模塊具有兩個主要特征:   (1)對於擴展是開放的(Open for extension)。這意味著模塊的行為是可以擴展的。當應用的需求改變時,我們可以對模塊進行擴展,使其具有滿足那些改變的新行為。也就是說,我們可以改變模塊的功能。   (2)對於修改是關閉的(Closed for modification)。對模塊行為進行擴展時,不必改動模塊的源代碼或者二進制代碼。模塊的二進制可執行版本,無論是可鏈接的庫、DLL或者.EXE文件,都無需改動。

  實現開開放封閉原則的核心思想就是對抽象編程,而不對具體編程,因為抽象相對穩定。讓類依賴於固定的抽象,所以修改就是封閉的;而通過面向對象的繼承和多態機制,又可以實現對抽象類的繼承,通過覆寫其方法來改變固有行為,實現新的拓展方法,所以就是開放的。

  “需求總是變化”沒有不變的軟件,所以就需要用封閉開放原則來封閉變化滿足需求,同時還能保持軟件內部的封裝體系穩定,不被需求的變化影響。

接口隔離原則(ISP:Interface Segregation Principle)

  對於接口隔離原則,其核心思想是:使用多個小的專門的接口,而不要使用一個大的總接口。

 具體而言,接口隔離原則體現在:接口應該是內聚的,應該避免“胖”接口。一個類對另外一個類的依賴應該建立在最小的接口上,不要強迫依賴不用的方法,這是一種接口汙染。

  使用多個專門的接口比使用單一的總接口要好。   一個類對另外一個類的依賴性應當是建立在最小的接口上的。   一個接口代表一個角色,不應當將不同的角色都交給一個接口。沒有關系的接口合並在一起,形成一個臃腫的大接口,這是對角色和接口的汙染。   “不應該強迫客戶依賴於它們不用的方法。接口屬於客戶,不屬於它所在的類層次結構。”這個說得很明白了,再通俗點說,不要強迫客戶使用它們不用的方法,如果強迫用戶使用它們不使用的方法,那麽這些客戶就會面臨由於這些不使用的方法的改變所帶來的改變。       舉例說明:   使用場合,提供調用者需要的方法,屏蔽不需要的方法.滿足接口隔離原則.比如說電子商務的系統,有訂單這個類,有三個地方會使用到,   一個是門戶,只能有查詢方法,   一個是外部系統,有添加訂單的方法,   一個是管理後臺,添加刪除修改查詢都要用到.   根據接口隔離原則(ISP),一個類對另外一個類的依賴性應當是建立在最小的接口上.   也就是說,對於門戶,它只能依賴有一個查詢方法的接口.

下面是實現的代碼

interface IOrderForPortal{
    String getOrder();
}
interface IOrderForOtherSys{
    String insertOrder();
    String getOrder();
}
interface IOrderForAdmin{ //extendsIOrderForPortal,IOrderForOtherSys
    String deleteOrder();
    String updateOrder();
    String insertOrder();
    String getOrder();
}
/*
interface IOrderForPortal{
String getOrder();
}
interface IOrderForOtherSys{
String insertOrder();
}
interface IOrderForAdmin extendsIOrderForPortal,IOrderForOtherSys{
String updateOrder();
String deleteOrder();
}
*/
class Order implements IOrderForPortal,IOrderForOtherSys,IOrderForAdmin{
    private Order(){
    //--什麽都不幹,就是為了不讓直接 new,防止客戶端直接New,然後訪問它不需要的方法.
    }
    //返回給Portal
    public static IOrderForPortal getOrderForPortal(){
    return (IOrderForPortal)new Order();
    }
    //返回給OtherSys
    public static IOrderForOtherSys getOrderForOtherSys(){
        return (IOrderForOtherSys)new Order();
    }
    //返回給Admin
    public static IOrderForAdmin getOrderForAdmin(){
        return (IOrderForAdmin)new Order();
    }
    //--下面是接口方法的實現.只是返回了一個String用於演示
    public String getOrder(){
        return "implemented getOrder";
    }
    public String insertOrder(){
        return "implementedinsertOrder";
    }
    public String updateOrder(){
        return "implementedupdateOrder";
    }
    public String deleteOrder(){
        return "implementeddeleteOrder";
    }
}
public class TestCreateLimit{
    public static void main(String[] args){
        IOrderForPortal orderForPortal =Order.getOrderForPortal();
        IOrderForOtherSys orderForOtherSys =Order.getOrderForOtherSys();
        IOrderForAdmin orderForAdmin = Order.getOrderForAdmin();
        System.out.println("Portal門戶調用方法:"+orderForPortal.getOrder());
        System.out.println("OtherSys外部系統調用方法:"+orderForOtherSys.insertOrder());
        System.out.println("Admin管理後臺調用方法:"+orderForAdmin.getOrder()+";"+orderForAdmin.insertOrder()+";"+orderForAdmin.updateOrder()+";"+orderForAdmin.deleteOrder());
    }
}

這樣就能很好的滿足接口隔離原則了,調用者只能訪問它自己的方法,不能訪問到不應該訪問的方法.

裏氏替換原則(LSP:Liskov Substitution Principle)

  對於Liskov替換原則,其核心思想是:子類必須能夠替換其基類。這一思想體現為對繼承機制的約束規範,只有子類能夠替換基類時,才能保證系統在運行期內識別子類,這是保證繼承復用的基礎。在父類和子類的具體行為中,必須嚴格把握繼承層次中的關系和特征,將基類替換為子類,程序的行為不會發生任何變化。同時,這一約束反過來則是不成立的,子類可以替換基類,但是基類不一定能替換子類。

 Liskov替換原則,主要著眼於對抽象和多態建立在繼承的基礎上,因此只有遵循了Liskov替換原則,才能保證繼承復用是可靠地。實現的方法是面向接口編程:將公共部分抽象為基類接口或抽象類,通過Extract Abstract Class,在子類中通過覆寫父類的方法實現新的方式支持同樣的職責。

  Liskov替換原則是關於繼承機制的設計原則,違反了Liskov替換原則就必然導致違反開放封閉原則。

 Liskov替換原則能夠保證系統具有良好的拓展性,同時實現基於多態的抽象機制,能夠減少代碼冗余,避免運行期的類型判別。

舉例說明:長方形和正方形

  對於長方形的類,如果它的長寬相等,那麽它就是一個正方形,因此,長方形類的對象中有一些正方形的對象。對於一個正方形的類,它的方法有個setSide和getSide,它不是長方形的子類,和長方形也不會符合LSP。

//長方形類:
public class Rectangle{
    setWidth(int width){
        this.width=width;
    }
    setHeight(int height){
        this.height=height
    }
}
//正方形類:
public class Square{
    setWidth(int width){
        this.width=width;
        this. height=width;
    }
    setHeight(int height){
       this.setWidth(height);
    }
}
//例子中改變邊長的函數:
public void resize(Rectangle r){
    while(r.getHeight()<=r.getWidth){
        r.setHeight(r.getWidth+1);
    }
}    

  那麽,如果讓正方形當做是長方形的子類,會出現什麽情況呢?我們讓正方形從長方形繼承,然後在它的內部設置width等於height,這樣,只要width或者height被賦值,那麽width和height會被同時賦值,這樣就保證了正方形類中,width和height總是相等的.現在我們假設有個客戶類,其中有個方法,規則是這樣的,測試傳入的長方形的寬度是否大於高度,如果滿足就停止下來,否則就增加寬度的值。現在我們來看,如果傳入的是基類長方形,這個運行的很好。根據LSP,我們把基類替換成它的子類,結果應該也是一樣的,但是因為正方形類的width和height會同時賦值,條件總是滿足,這個方法沒有結束的時候,也就是說,替換成子類後,程序的行為發生了變化,它不滿足LSP。

  我們構造一個抽象的四邊形類,把長方形和正方形共同的行為放到這個四邊形類裏面,讓長方形和正方形都是它的子類,問題就OK了。對於長方形和正方形,取width和height是它們共同的行為,但是給width和height賦值,兩者行為不同,因此,這個抽象的四邊形的類只有取值方法,沒有賦值方法。上面的例子中那個方法只會適用於不同的子類,LSP也就不會被破壞。

依賴倒置原則(DIP:Dependence Inversion Principle) 

  對於依賴倒置原則,其核心思想是:依賴於抽象。具體而言就是高層模塊不依賴於底層模塊,二者都同依賴於抽象;抽象不依賴於具體,具體依賴於抽象。

 我們知道,依賴一定會存在於類與類、模塊與模塊之間。當兩個模塊之間存在緊密的耦合關系時,最好的方法就是分離接口和實現:在依賴之間定義一個抽象的接口使得高層模塊調用接口,而底層模塊實現接口的定義,以此來有效控制耦合關系,達到依賴於抽象的設計目標。

  抽象的穩定性決定了系統的穩定性,因為抽象是不變的,依賴於抽象是面向對象設計的精髓,也是依賴倒置原則的核心。

 依賴於抽象是一個通用的原則,而某些時候依賴於細節則是在所難免的,必須權衡在抽象和具體之間的取舍,方法不是一層不變的。依賴於抽象,就是對接口編程,不要對實現編程。

面向對象的三大特性與五大基本原則