面向對象六大原則

来源:https://www.cnblogs.com/wangbaicheng1477865665/archive/2018/03/10/8540122.html
-Advertisement-
Play Games

面向對象六大原則 單一職責原則——SRP 開閉原則——OCP 里式替換原則——LSP 依賴倒置原則——DIP 介面隔離原則——ISP 迪米特原則——LOD ...


 

這是設計模式系列開篇的第一篇文章。也是我學習設計模式過程中的總結。這篇文章主要講的是面向對象設計中,我們應該遵循的六大原則。只有掌握了這些原則,我們才能更好的理解設計模式。
我們接下來要介紹以下6個內容。

  1. 單一職責原則——SRP
  2. 開閉原則——OCP
  3. 里式替換原則——LSP
  4. 依賴倒置原則——DIP
  5. 介面隔離原則——ISP
  6. 迪米特原則——LOD

單一職責原則

單一職責原則的定義是就一個類而言,應該僅有一個引起他變化的原因。也就是說一個類應該只負責一件事情。如果一個類負責了方法M1,方法M2兩個不同的事情,當M1方法發生變化的時候,我們需要修改這個類的M1方法,但是這個時候就有可能導致M2方法不能工作。這個不是我們期待的,但是由於這種設計卻很有可能發生。所以這個時候,我們需要把M1方法,M2方法單獨分離成兩個類。讓每個類只專心處理自己的方法。
單一職責原則的好處如下:

  1. 可以降低類的複雜度,一個類只負責一項職責,這樣邏輯也簡單很多
  2. 提高類的可讀性,和系統的維護性,因為不會有其他奇怪的方法來干擾我們理解這個類的含義
  3. 當發生變化的時候,能將變化的影響降到最小,因為只會在這個類中做出修改。

開閉原則

開閉原則和單一職責原則一樣,是非常基礎而且一般是常識的原則。開閉原則的定義是軟體中的對象(類,模塊,函數等)應該對於擴展是開放的,但是對於修改是關閉的。
當需求發生改變的時候,我們需要對代碼進行修改,這個時候我們應該儘量去擴展原來的代碼,而不是去修改原來的代碼,因為這樣可能會引起更多的問題。
這個準則和單一職責原則一樣,是一個大家都這樣去認為但是又沒規定具體該如何去做的一種原則。
開閉原則我們可以用一種方式來確保他,我們用抽象去構建框架,用實現擴展細節。這樣當發生修改的時候,我們就直接用抽象了派生一個具體類去實現修改。

里氏替換原則

里氏替換原則是一個非常有用的一個概念。他的定義

如果對每一個類型為T1的對象o1,都有類型為T2的對象o2,使得以T1定義的所有程式P在所有對象o1都替換成o2的時候,程式P的行為都沒有發生變化,那麼類型T2是類型T1的子類型。

這樣說有點複雜,其實有一個簡單的定義

所有引用基類的地方必須能夠透明地使用其子類的對象。

里氏替換原則通俗的去講就是:子類可以去擴展父類的功能,但是不能改變父類原有的功能。他包含以下幾層意思:

  1. 子類可以實現父類的抽象方法,但是不能覆蓋父類的非抽象方法。
  2. 子類可以增加自己獨有的方法。
  3. 當子類的方法重載父類的方法時候,方法的形參要比父類的方法的輸入參數更加寬鬆。
  4. 當子類的方法實現父類的抽象方法時,方法的返回值要比父類更嚴格。

里氏替換原則之所以這樣要求是因為繼承有很多缺點,他雖然是復用代碼的一種方法,但同時繼承在一定程度上違反了封裝。父類的屬性和方法對子類都是透明的,子類可以隨意修改父類的成員。這也導致了,如果需求變更,子類對父類的方法進行一些覆寫的時候,其他的子類無法正常工作。所以里氏替換法則被提出來。
確保程式遵循里氏替換原則可以要求我們的程式建立抽象,通過抽象去建立規範,然後用實現去擴展細節,這個是不是很耳熟,對,里氏替換原則和開閉原則往往是相互依存的。

依賴倒置原則

依賴倒置原則指的是一種特殊的解耦方式,使得高層次的模塊不應該依賴於低層次的模塊的實現細節的目的,依賴模塊被顛倒了。
這也是一個讓人難懂的定義,他可以簡單來說就是

高層模塊不應該依賴底層模塊,兩者都應該依賴其抽象
抽象不應該依賴細節
細節應該依賴抽象

在Java 中抽象指的是介面或者抽象類,兩者皆不能實例化。而細節就是實現類,也就是實現了介面或者繼承了抽象類的類。他是可以被實例化的。高層模塊指的是調用端,底層模塊是具體的實現類。在Java中,依賴倒置原則是指模塊間的依賴是通過抽象來發生的,實現類之間不發生直接的依賴關係,其依賴關係是通過介面是來實現的。這就是俗稱的面向介面編程。
我們下麵有一個例子來講述這個問題。這個例子是工人用錘子來修理東西。我們的代碼如下:

public class Hammer {
    public String function(){
        return "用錘子修理東西";
    }
}

public class Worker {
    public void fix(Hammer hammer){
        System.out.println("工人" + hammer.function());
    }


    public static void main(String[] args) {
        new Worker().fix(new Hammer());
    }
}

這個是一個很簡單的例子,但是如果我們要新增加一個功能,工人用 螺絲刀來修理東西,在這個類,我們發現是很難做的。因為我們Worker類依賴於一個具體的實現類Hammer。所以我們用到面向介面編程的思想,改成如下的代碼:

public interface Tools {
    public String function();
}

然後我們的Worker是通過這個介面來於其他細節類進行依賴。代碼如下:

public class Worker {
    public void fix(Tools tool){
        System.out.println("工人" + tool.function());
    }


    public static void main(String[] args) {
        new Worker().fix(new Hammer());
        new Worker().fix(new Screwdriver());

    }
}

我們的Hammer類與Screwdriver類實現這個介面

public class Hammer implements Tools{
    public String function(){
        return "用錘子修理東西";
    }
}

public class Screwdriver implements Tools{
    @Override
    public String function() {
        return "用螺絲刀修理東西";
    }
}

這樣,通過面向介面編程,我們的代碼就有了很高的擴展性,降低了代碼之間的耦合度,提高了系統的穩定性。

介面隔離原則

介面隔離原則的定義是

客戶端不應該依賴他不需要的介面

換一種說法就是類間的依賴關係應該建立在最小的介面上。這樣說好像更難懂。我們通過一個例子來說明。我們知道在Java中一個具體類實現了一個介面,那必然就要實現介面中的所有方法。如果我們有一個類A和類B通過介面I來依賴,類B是對類A依賴的實現,這個介面I有5個方法。但是類A與類B只通過方法1,2,3依賴,然後類C與類D通過介面I來依賴,類D是對類C依賴的實現但是他們卻是通過方法1,4,5依賴。那麼是必在實現介面的時候,類B就要有實現他不需要的方法4和方法5 而類D就要實現他不需要的方法2,和方法3。這簡直就是一個災難的設計。
所以我們需要對介面進行拆分,就是把介面分成滿足依賴關係的最小介面,類B與類D不需要去實現與他們無關介面方法。比如在這個例子中,我們可以把介面拆成3個,第一個是僅僅由方法1的介面,第二個介面是包含2,3方法的,第三個介面是包含4,5方法的。
這樣,我們的設計就滿足了介面隔離原則。
以上這些設計思想用英文的第一個字母可以組成SOLID ,滿足這個5個原則的程式也被稱為滿足了SOLID準則。

迪米特原則

迪米特原則也被稱為最小知識原則,他的定義

一個對象應該對其他對象保持最小的瞭解。

因為類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大,所以這也是我們提倡的軟體編程的總的原則:低耦合,高內聚。
迪米特法則還有一個更簡單的定義

只與直接的朋友通信。首先來解釋一下什麼是直接的朋友:每個對象都會與其他對象有耦合關係,只要兩個對象之間有耦合關係,我們就說這兩個對象之間是朋友關係。耦合的方式很多,依賴、關聯、組合、聚合等。其中,我們稱出現成員變數、方法參數、方法返回值中的類為直接的朋友,而出現在局部變數中的類則不是直接的朋友。也就是說,陌生的類最好不要作為局部變數的形式出現在類的內部。

這裡我們可以用一個現實生活中的例子來講解一下。比如我們需要一張CD,我們可能去音像店去問老闆有沒有我們需要的那張CD,老闆說現在沒有,等有的時候你們來拿就行了。在這裡我們不需要關心老闆是從哪裡,怎麼獲得的那張CD,我們只和老闆(直接朋友)溝通,至於老闆從他的朋友那裡通過何種條件得到的CD,我們不關心,我們不和老闆的朋友(陌生人)進行通信,這個就是迪米特的一個應用。說白了,就是一種中介的方式。我們通過老闆這個中介來和真正提供CD的人發生聯繫。

總結

到這裡,面向對象的六大原則,就寫完了。我們看出來,這些原則其實都是應對不斷改變的需求。每當需求變化的時候,我們利用這些原則來使我們的代碼改動量最小,而且所造成的影響也是最小的。但是我們在看這些原則的時候,我們會發現很多原則並沒有提供一種公式化的結論,而即使提供了公式化的結論的原則也只是建議去這樣做。這是因為,這些設計原則本來就是從很多實際的代碼中提取出來的,他是一個經驗化的結論。怎麼去用它,用好他,就要依靠設計者的經驗。否則一味者去使用設計原則可能會使代碼出現過度設計的情況。大多數的原則都是通過提取出抽象和介面來實現,如果發生過度的設計,就會出現很多抽象類和介面,增加了系統的複雜度。讓本來很小的項目變得很龐大,當然這也是Java的特性(任何的小項目都會做成中型的項目)

原文:

http://www.cnblogs.com/qifengshi/p/5709594.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 昨天一個話題說關於AngularJS2以後版本的兩個小技巧,不料引出了另外一個話題,話題起始很簡單: “很多的前端框架並不複雜,比如JQuery,引入即用,實時看到效果,多好。到了Angular2一直到現在的版本5,一點改進沒有,還要編譯,還要部署,原有的JS腳本也不能用了。” 細想起來,這個話題的 ...
  • 1.stylus 1.1用法 註意: &:hover 在pc端是滑鼠懸浮,在移動端變成點擊 ...
  • Ajax:Aysnchronous Javascript and xml(非同步JS和XML技術) 1. Ajax是什麼(數據交互) 2. 瀏覽器(客戶端)和伺服器 3. 瀏覽器和伺服器的交互 4. 註釋 5.onreadystatechange 和 onload 6.GET和POST ...
  • 首先先推薦一篇博文:http://blog.csdn.net/jasonzds/article/details/53717501 這篇博文很清晰的說明瞭Promise的用法,這裡來簡單總結一下: Promise主要用來讓連續的回調變得清晰。比如: 連續獲取豆瓣電影數據: 這樣寫的話代碼一複雜就會非常 ...
  • 這個筆記,僅僅演示dojo/fx模塊的slideTo()方法的簡單使用。 有關該模塊的用法,見API;有關Dojo的動畫、效果,見頁面 效果 和 動畫 1. 頁面組織 html部分同筆記3,js部分如下: 如果不想看,直接抽取關鍵代碼: 2. 代碼解釋 fx模塊調用slideTo方法,傳參是一個Ob ...
  • html頁面: note:註意不要使用form標簽 JS事件: ...
  • 今天心血來潮,打開了sublime想玩玩react,然後大家都知道的先引入一大串 就是在百度靜態資源庫里找到的。 然後貼html代碼 對的,沒錯,就這麼一行,畢竟只是測試嘛 然後js代碼 大家一定要註意,在script的開頭標簽里,一定要註明,否則瀏覽器會報錯,解析不了。 就按照這樣的代碼,照理來說 ...
  • 1.url: 要求為String類型的參數,(預設為當前頁地址)發送請求的地址。 2.type: 要求為String類型的參數,請求方式(post或get)預設為get。註意其他http請求方法,例如put和delete也可以使用,但僅部分瀏覽器支持。 3.timeout: 要求為Number類型的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...