【面向對象設計原則】之介面隔離原則(ISP)

来源:http://www.cnblogs.com/vaiyanzi/archive/2017/05/27/6913529.html
-Advertisement-
Play Games

介面隔離原則(Interface Segregation Principle, ISP):使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。 從介面隔離原則的定義可以看出,他似乎跟SRP有許多相似之處。 是的其實ISP和SRP都是強調職責的單一性, 介面隔離原則告訴我們 ...


介面隔離原則(Interface  Segregation Principle, ISP):使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。

從介面隔離原則的定義可以看出,他似乎跟SRP有許多相似之處。 是的其實ISP和SRP都是強調職責的單一性, 介面隔離原則告訴我們在定義介面的時候要根據職責定義“較小”的介面,不要定義“高大全”的介面。也就是說介面要儘可能的職責單一,這樣更容易復用,暴露給客戶端的方法更具有“針對性”, 比如定義一個介面包括一堆訪問資料庫的方法, 有包括一堆訪問網路的方法,還包括一些許可權認證的方法。 把這麼一攤子風牛馬不相及的方法封裝到一個介面裡面,顯然是不合適的, 如果客戶程式只想用到數據訪問的一些功能,但是調用介面的時候你把訪問網路的方法和許可權認證的方法暴露給客戶,這使得客戶程式感到“疑惑”,那麼這個介面就不ISP,它很顯然的構成了介面污染。

註意: 這裡所說的介面是廣義上的介面,他是一組契約, 是提供給程式交互的一組約定,並非各種語言interface 關鍵字定義的一組方法的結集合。但是這裡所說的介面可以用各種語言的關鍵字interface 來定義,當然也可以用抽象類,類等等來定義。

假設有個客戶提出了軟體系統的需求:

1. 用戶可以使用第三方QQ,微信,微博登錄到系統。

2.系統中包括人員管理人員管理。

3.訪問第三方的API獲取一些數據。

好了拿到這個需求後首先經過分析,簡單的原型設計,資料庫設計之後開始編寫代碼了。 通常第一步定義介面。很快介面就定義出來瞭如下:

    public interface IObject
    {
        void Connection(string connectionString);
        SqlDataReader ExcuteSql(string sql);
        string LoginWithQQ(string token);
        string LoginWithWeibo(string token);
        string LoginWithWeiXin(string token);
        string GetDataFromAPI(string url, string token);
    }

這個看起來還不錯,介面已經定義了,寫個具體類繼承一下這個介面並實現所有的方法,現在就可以實現業務,寫界面了。 等過了幾天客戶說 在給我加上支付寶登錄。那好再加一個支付寶登錄介面,代碼現在長這樣子:

    public interface IObject
    {
        void Connection(string connectionString);
        SqlDataReader ExcuteSql(string sql);
        string LoginWithQQ(string token);
        string LoginWithWeibo(string token);
        string LoginWithWeiXin(string token);
        string GetDataFromAPI(string url, string token);
        string LoginWithAlipay(string token);
    }

再在實現類中實現一下LoginWithAlipay方法 就好了。

時間在推移,一天客戶說再給我加個百度登錄,好吧套路有了加一個就是了,有啥了不起。 時間依舊。。。 客戶說加個 facebook 登錄, 。。。加個 Linkedin。。。, 尼瑪 沒完沒了了, 現在介面已經變成這樣子了:

    public interface IObject
    {
        void Connection(string connectionString);
        SqlDataReader ExcuteSql(string sql);
        string LoginWithQQ(string token);
        string LoginWithWeibo(string token);
        string LoginWithWeiXin(string token);
        string GetDataFromAPI(string url, string token);
        string LoginWithAlipay(string token);
        string LoginWithTwitter(string token);
        string LoginWithFaceBook(string token);
        string LoginWithRenRen(string token);
        string LoginWithBaidu(string token);
        string LoginWithDropbox(string token);
        string LoginWithGithub(string token);
        //這裡省略10000字       
stringLoginWithLinkedin(string token);
    }

有一天這個介面自己都不想看了,太多方法了,更何況實現類中的代碼都七八千行了。

於是決定重構, 現在回頭看看這個介面早就應該重構了,甚至一開始定義的時候就應該拆分,介面的名字都不知道怎麼命名(一般在寫代碼的時候類,介面,方法的名字不知道怎麼命名的時候就是該重構的時候了)竟然起了IObject這麼奇葩的名字,這個設計顯然是爛到家了, 他幾乎違背了我們講過的所有設計原則, 必須到了要重構的時候了。

來吧,重構吧,經過分析第一步先根據功能來劃分將IObject介面拆分成三個“小”介面:

1.資料庫操作相關的抽取到一個介面中(IDatabaseProvider)。

2.第三方API調用相關的方法抽取到一個介面中(IThirdpartyAPIProvider)。

3.第三方登陸相關的方法抽取到一個介面中(IThirdpartyAuthenticationProvider)。

現在代碼變成這個樣子:

    public interface IDatabaseProvider
    {
        SqlDataReader ExcuteSql(string sql);
        string LoginWithQQ(string token);
    }

    public interface IThirdpartyAPIProvider
    {
        string Get(string url, string token);
    }

    public interface IThirdpartyAuthenticationProvider
    {
        string LoginWithQQ(string token);
        string LoginWithWeibo(string token);
        string LoginWithWeiXin(string token);
        string LoginWithAlipay(string token);
        string LoginWithTwitter(string token);
        string LoginWithFaceBook(string token);
        string LoginWithRenRen(string token);
        string LoginWithBaidu(string token);
        string LoginWithDropbox(string token);
        string LoginWithGithub(string token);
        //這裡省略10000字
        string LoginWithLinkedin(string token);
    }

這下看起來好多了, 但是IThirdpartyAuthenticationProvider 代碼還很多,還很醜陋,有沒有辦法再進一步重構呢? 答案是肯定。 第二步 我們可以將第三方登錄的介面中的LogigWithxxx方法提到一個單獨的介面中,其他具體站點的介面再繼承這個介面,代碼如下:

    public interface IThirdpartyAuthenticationProvider
    {
        string Login(string token);
    }

    public interface IQQAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IWeiboAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IWeiXinAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IAlipayAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface ITwitterAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IFaceBookAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IRenRenAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IBaiduAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    public interface IDropboxAuthenticationProvider : IThirdpartyAuthenticationProvider { }
    public interface IGitHubAuthenticationProvider:IThirdpartyAuthenticationProvider{}
    //這裡省略10000字
    public interface ILinkedinAuthenticationProvider : IThirdpartyAuthenticationProvider { }
}

這這下就好多了。 我們分析一下重構後的代碼有什麼好處:

1. 介面的職責更單一了,調用目標更清晰了,每一個介面就專門做一件事情。符合SRP了。

2. 在操作資料庫的時候不會在IDatabase介面中調到其它的第三方API調用和第三方登錄認證相關的方法,每一個幾口更專註了。符合ISP了。

3.在添加新的第三方登錄的時候不需要在修改原來的實現 類了,核心業務邏輯只需要加一個介面和介面的實現類就可以了。符合OCP了。

4. 提升了代碼的穩定性,可維護性和可擴展性。

當然任何事情都具有兩面性,如果將一件好事做到極端有可能就會走向反面, 比方說定義一個User實體的介面:

public interface IIdProperty { int Id { get; set; } }
public interface IFirstNameProperty { string FirstName { get; set; } }
public interface ILastNameProperty { string LastName { get; set; } }
public interface IAgeProperty { int Age { get; set; } }
public interface IBirthdayProperty { DateTime Birthday { get; set; } }

public class User:IIdProperty,IFirstNameProperty,ILastNameProperty,IAgeProperty,IBirthdayProperty
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public DateTime Birthday { get; set; }
}

把每個屬性都定義成一個介面那就不可取了,也是沒有意義的,反而給維護或擴展帶來不必要的麻煩,這就是使用介面時要註意的地方:

在使用介面時要註意控制介面的粒度,介面定義的粒度不能太細,也不能太粗。 介面粒度太細,系統中就會出現介面泛濫,介面和實現類急劇膨脹,反而不易維護;介面粒度太粗,就會違背ISP,系統的靈活性就會降低,不易維護和擴展。

關聯閱讀:

【面向對象設計原則】之原則概述

【面向對象設計原則】之單一職責原則(SRP)

【面向對象設計原則】之開閉原則(OCP)

【面向對象設計原則】之里氏替換原則(LSP)

【面向對象設計原則】之依賴倒置原則(DIP)


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

-Advertisement-
Play Games
更多相關文章
  • ★★ 輸入文件:counttree.in 輸出文件:counttree.out 簡單對比 時間限制:1 s 記憶體限制:128 MB 【題目描述】 【輸入格式】 輸入第一行包含一個整數N,以下N行每行包含一個整數,其中第i行的整數表示編號為i的節點的父親節點的編號,根的父親節點編號為0。 【輸出格式】 ...
  • 誤解一:JavaScript是Java的簡易版 JavaScript是一種在網頁中使用的腳本語言,它的原名叫做LiveScript。JavaScript的語法與Java類似。除此之外,他們再無任何關係。JavaScript的一個子集已經標準化為ECMA-262,它更加緊密地與瀏覽器集成在一起。 誤解 ...
  • 一、MFC的概念和作用 1、什麼是MFC? 全稱:Microsoft Foundation Class Library(微軟基礎類庫) 1-MFC從硬碟存在形式來說就是一個庫(靜態MFC庫、動態MFC庫) 2-MFC從原理來說還是一個程式框架 2、為什麼使用MFC? 基於框架編程,提高工作效率,減少 ...
  • 本文詳細講解Python的命名空間,作用域,以及在使用中的一些常見困惑。 ...
  • 預計分數:T1:40AC+60TLE T2:40AC+60TLE T3:10AC+90TLE 總分=90 實際分數:T1:100AC+0TLE T2:80AC+20TLE T3:20AC+80TLE 總分=200 感想:數據水的一逼!! 題目實際難度: T1:提高/提高+ T2:提高+ T3:提高+ ...
  • 《設計模式:可復用面向對象軟體的基礎》是引導讀者走出軟體設計迷宮的指路明燈,凝聚了軟體開發界幾十年設計經驗的結晶。四位面向對象領域專家精心選取了具價值的設計實踐,加以分類整理和命名,並用簡潔而易於重用的形式表達出來。本書已經成為面向對象技術人員的聖經和詞典,書中定義的23個模式逐漸成為開發界技術交流 ...
  • 1.什麼是模塊化 將實現不同功能的代碼分別存放到不同的文件、類、方法中,每一個文件、類、方法都是一個實現單一功能的模塊。 2.為什麼使用模塊化 模塊化的文件、類、方法功能單一,可以相對獨立存在,不僅降低了對其他對象的依賴,而且層次清晰,便於維護。 3.模塊化的具體實現方法 通過增加模塊數目減小單個文 ...
  • IBM Rational Software Architect(RSA) -- IBM軟體開發平臺的一部分 – 是IBM在2003年二月併購Rational以來,首次發佈的Rational產品。改進過的軟體開發平臺在集成和易用性上達到一個新的層次。算是Rational Rose是的一個替代品。 Ra ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...