【面向對象設計原則】之介面隔離原則(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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...