超前的設計或者過度的設計都不是良好的設計,很多時候我們等到代碼在第一次變化的時候可以及時作出反應。
What
就一個類(接口、結構體、方法等等)而言,應該僅有一個引起它變化的原因。
Why
軟件設計真正要做的許多內容,就是發現職責並把那些職責互相分離。單一職責原則可以使類的復雜度降低,實現什么職責都有清晰明確的定義;類的可讀性提高,復雜度降低(復雜度降低肯定可讀性提高);可讀性提高了,代碼就更容易維護;變更(需求是肯定會變的,程序員都知道)引起的風險(包括測試的難度,以及需要測試的范圍)降低。
How
需求:實現拍照和播放音樂,那先定義兩個功能接口(參照《大話設計模式》)
//具有照相的功能的接口 interface IPhotograph { void Photograph(); }
//具有播放音樂功能的接口 interface IPlayMusic { void PlayMusic(); }
不遵循單一原則的設計,播放音樂及拍照功能的改變都會引起變化
//實現照相、播放音樂的手機類 public class MobilePhone : IPhotograph, IPlayMusic { //拍照 public void Photograph() { Console.WriteLine("拍照片"); } //播放音樂 public void PlayMusic() { Console.WriteLine("播放音樂"); } }
class Program { static void Main(string[] args) { IPlayMusic musicPlayer; IPhotograph photographer; MobilePhone phone = new MobilePhone(); musicPlayer = phone; photographer = phone; //播放音樂 musicPlayer.PlayMusic(); //拍照 photographer.Photograph(); Console.ReadLine(); } }
遵循單一原則的設計,引發改變的只有播放音樂功能的變化
//實現播放音樂功能的音樂播放器類 class MusicPlayer : IPlayMusic { public void PlayMusic() { Console.WriteLine("播放音樂"); } }
遵循單一原則的設計,引發改變的只有攝像功能的變化
//實現照相功能的攝像機類 class Carmera : IPhotograph { public void Photograph() { Console.WriteLine("拍照片"); } }
class Program { static void Main(string[] args) { IPlayMusic musicPlayer; IPhotograph photographer; //MobilePhone phone = new MobilePhone(); //musicPlayer = phone; //photographer = phone; musicPlayer = new MusicPlayer(); photographer = new Carmera(); //播放音樂 musicPlayer.PlayMusic(); //拍照 photographer.Photograph(); Console.ReadLine(); } }
糟糕的設計會造成什么樣的后果呢?讓我們來設想一下,有一天我們的需求發生了變化(需求變化-程序員一生的敵人兼朋友),現有擁有了一部手機,有拍照的功能以及播放音樂的功能,滿足了現有的需求,突然有一天覺得簡單的拍照功能已經不能滿足了,
現在需要能夠拍攝高清圖片的照相功能,那么怎么辦呢?換手機唄,恩找一個支持高清拍照功能的手機。那么好的,需求又變了,現在想要能播放高品質音樂的功能,但是新換的支持高清拍攝的手機的硬件不支持高品質音樂播放,好的,繼續換手機,前提是還要
支持拍攝高清照片。相信現在已經能夠看出一些端倪了,這兩個職責無論哪一個發生了變化,你都要去改變手機,現在只有兩個職責,誇張一點說,如果有十個職責呢?那么豈不是要天天換手機,要么就不滿足需求變化。
既然我們看到了糟糕的設計,現在我們回到單一職責上,既然你的需求是拍照片和播放音樂,那么我給你一台相機還有一台音樂播放器,哪個功能需要改變你就換哪個,以后你要換的時候也不必去考慮其他功能,只需要關心引起你自己變化的原因。如果拍照
功能發生改變,我們就去改變照相機,播放音樂功能需要改變我們就去改變音樂播放器。我們不需要去考慮播放高品質音樂是不是會對拍攝高清圖片的功能造成影響。
我們一定要遵循單一職責原則嗎?在現有的需求上能做到當然可以去做,但是往往有的時候,需求不是在設計的時候發生改變,而是一定程度之后,你已經有了一定的代碼量了,可能修改的開銷很高,這個時候就仁者見仁智者見智。就如上述,若是將手機類
拆分,則影響了底層調用的實現,也需要修改,弱是調用的地方太多,那么修改的地方也會很多,若是發布了,改起來也不是很方便,但是當然,也有一定的手法來做這件事情,比如手機類保留,讓手機類擁有一個攝像機類對象和一個音樂播放器類對象,然后播放
音樂方法則調用音樂播放器類實例的播放音樂功能,照相功能則調用攝像機類實例的照相功能,這樣可以在不影響原有的東西的基礎上又遵循原則。