解釋
The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.
意思就是說一個類別不應該被強迫實作一個它不需要的方法。
其實換句話說是限制你不要過度膨脹介面中定義的方法,盡量是能夠滿足使用的情境即可,別想要把全部的東西都塞進介面中,以下用個簡單的例子來描述。
Lab
我們想在系統裡面建立一個 Log 服務,裡面大概會提供以下的服務
- 寫入 Log 至 文字檔/資料庫、或呼叫Log時透過Email寄送
ILogger.cs
我們開始著手先根據上面的需求建立一個基本的介面
1 2 3 4 5 6 7 8 9 10 11
| public interface ILogger { string Subject { get; set; }
string Message { get; set; }
List<string> MailAddresses { get; set; }
void WriteLog(string message);
}
|
然後實作兩種不同類型的 Log 服務
MailLogger.cs, DatabaseLogger.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class MailLogger : ILogger { public string Subject { get; set; }
public string Message { get; set; }
public List<string> Addresses { get; set; }
public void WriteLog() { var mailMessage = new MailMessage(); mailMessage.Subject = Subject; mailMessage.Body = Message; foreach(var add in Addresses) { mailMessage.To.Add(add); } var client = new SmtpClient(); client.Send(mailMessage); } }
public class DatabaseLogger : ILogger { public string Subject { get; set; }
public string Message { get; set; }
public List<string> Addresses { get; set; }
public void WriteLog() { } }
|
看完了兩個實作之後,是不是發現有一點怪怪的?
這個怪怪的地方就是 DatabaseLogger
裡面為什麼要有 List<string> Addresses {get;set;}
因為他完全用不到。
所以 ISP 原則所告訴我們的就是類別不要強迫實作他不需要的方法,我們要對原本的方法做一點小修正,把不需要用到的屬性抽出來放到另外一個介面當中
ILogger.cs, IMessageLogger.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface ILogger { string Subject { get; set; }
string Message { get; set; }
void WriteLog();
}
public interface IMailLogger { List<string> Addresses { get; set; } }
|
DatabaseLogger.cs
把原本的 public List<string> Addresses { get; set; }
拿掉
1 2 3 4 5 6 7 8 9 10 11
| public class DatabaseLogger : ILogger { public string Subject { get; set; }
public string Message { get; set; }
public void WriteLog() { } }
|
MailLogger.cs
MailLogger
多實作剛剛新增的 IMailLogger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class MailLogger : ILogger, IMailLogger { public string Subject { get; set; }
public string Message { get; set; }
public List<string> Addresses { get; set; }
public void WriteLog() { var mailMessage = new MailMessage(); mailMessage.Subject = Subject; mailMessage.Body = Message; foreach(var add in Addresses) { mailMessage.To.Add(add); } var client = new SmtpClient(); client.Send(mailMessage); } }
|
這個例子比較單純一點,目的是想表現出 ISP 的宗旨 不要讓類別去實作他沒有用到的方法
同時也告訴我們在設計界面時,要注意不要想塞什麼東西進去就塞什麼東西進去,讓整個介面定義的接口過多,要負責的事情如果太多,未來在修改程式時如果發現子類別實作太多沒有必要實作的方法,就會要改很多的程式碼,建議在實作時若能夠早點發現這樣的狀況及早邊做重構。