四叫编、 接口隔離原則(Interface Segregation Principe,ISP)
類的依賴關(guān)系應(yīng)建立在最小接口上,不要都塞在一起时迫。即客戶端不應(yīng)該依賴它不需要的接口。
??根據(jù)上面的定義可以看出谓晌,對接口的建立要最小化掠拳,而不是依賴所有功能都塞在一起的大而全的接口。換種說法就是纸肉,方法盡量要細(xì)化溺欧,要少。當(dāng)然柏肪,也不要拆分成一個一個的姐刁,而是要把一些功能緊密綁的方法封裝起來,不要暴露太多細(xì)節(jié)预吆。哇龙填,這是不單一職責(zé)嗎?不對拐叉,它們的審視角度是不同的岩遗,一個是接口的依賴,要求接口的方法盡量要少凤瘦;一個是職責(zé)的分離宿礁,類和接口的職責(zé)要單一。接口隔離原則就是要求只提供盡可能小的接口蔬芥,需要高內(nèi)聚梆靖,不需要的行為要隱藏起來
。當(dāng)然笔诵,拆分接口時返吻,也需要滿足單一職責(zé)原則。
??來看一個手機(jī)的例子吧乎婿,手機(jī)在例子的世界還是很火爆的测僵,現(xiàn)在是至少人手一部了。看圖4.1
??定義了一個手機(jī)的接口捍靠,手機(jī)可以打電話沐旨,發(fā)短信,上網(wǎng)榨婆,玩游戲等磁携,然后使用了一個場景類People來使用手機(jī)×挤纾看起來是不是很完美谊迄,但仔細(xì)想想,ICellphone 這個接口有沒有最優(yōu)設(shè)計拖吼,是不是功能有點太多了鳞上,當(dāng)然,單從單一職責(zé)上考濾是沒有問題的吊档,加上接口隔離原則的話篙议,就不同了。時間回到手機(jī)剛誕生的年代怠硼,那時候的手機(jī)是不是只能打個電話鬼贱,發(fā)個短信。手機(jī)在迭代的過程中香璃,才出現(xiàn)了各種功能这难,上網(wǎng),玩游戲葡秒,在線支付等姻乓,當(dāng)今平板的出現(xiàn),除了上網(wǎng)等高級功能外眯牧,基礎(chǔ)的打電話蹋岩,發(fā)短信功能反而沒有了。這樣功能都封裝在一起学少,是不是就過度了剪个,而且都暴露到了使用場景中,不就有問題了嗎版确。那我們根據(jù)接口隔離原則重新修改一下類圖扣囊,如圖4.2所示
??這樣,不管以后使用什么手機(jī)绒疗,都可保持接口的穩(wěn)定侵歇。需要什么,就繼承什么吓蘑,如果是平板狠持,就不需要電話和短信井仰,保持了接口的最小化。來看一下代碼實現(xiàn):
/// <summary>
/// 基礎(chǔ)手機(jī)功能
/// 打電話、發(fā)短信
/// </summary>
public interface IBaseCellphone
{
void Call();
void Text();
}
/// <summary>
/// 上網(wǎng)玩游戲的功能
/// </summary>
public interface IOnlineGameCellphone
{
void Online();
void PlayGame();
}
/// <summary>
/// 現(xiàn)代手機(jī)蠢终,即可打電話,短信抵知,還可以上網(wǎng)玩游戲
/// </summary>
public class Cellphone : IBaseCellphone, IOnlineGameCellphone
{
public void Call()
{
Console.WriteLine("打電話");
}
public void Text()
{
Console.WriteLine("發(fā)短信");
}
public void Online()
{
Console.WriteLine("手機(jī)已連網(wǎng)");
}
public void PlayGame()
{
Console.WriteLine("開始玩游戲");
}
}
場景中使用
/// <summary>
/// 現(xiàn)實生活中的場景叁熔,使用手機(jī)
/// </summary>
public class People
{
public int Id { get; set; }
public string Name { get; set; }
/// <summary>
/// 一些人只使用手機(jī)的基本功能
/// </summary>
/// <param name="phone"></param>
public void UsePhone(IBaseCellphone cellphone)
{
Console.WriteLine("我是 {0},我只用基礎(chǔ)的功能", this.Name);
cellphone.Call();
cellphone.Text();
}
/// <summary>
/// 只想上網(wǎng)玩游戲
/// </summary>
/// <param name="cellphone"></param>
public void PlayOnlineGame(IOnlineGameCellphone cellphone)
{
Console.WriteLine("我是 {0}沸移,我只想上網(wǎng)玩游戲", this.Name);
cellphone.Online();
cellphone.PlayGame();
}
}
五痪伦、迪米特法則(Law of Demeter,LOD)
一個對象應(yīng)盡可能少的了解其它對象
??迪米特法則也稱最少知識原則(Least Knowledge Principle雹锣,LKP)网沾,名字雖不同,規(guī)則確是同樣的蕊爵。說的都是類與類之間的關(guān)系辉哥,一個類在使用其它類時,不需要知道其細(xì)節(jié)攒射,知道的越少越好醋旦,你內(nèi)部如何復(fù)雜多變,都是你自己的事情会放,我不關(guān)心饲齐,我只關(guān)心調(diào)用方法,取得結(jié)果咧最,其它的一概不關(guān)心捂人。迪米特法則的指導(dǎo)思想就是使類與類之間保持松耦合的關(guān)系
。主要包括如下:
- 不跟非直接的朋友說話
- 只是內(nèi)部服務(wù)的屬性矢沿,要封閉到內(nèi)部
- C#中
[Serializable]
特性滥搭,盡量少用,牽涉到序列化的問題
還是用實例來說話吧咨察,有一個關(guān)于學(xué)校的日常故事论熙,老師讓班長多關(guān)注下同學(xué)們的學(xué)習(xí)情況,有沒有好好聽課摄狱,認(rèn)真寫作業(yè)脓诡。讓我們來用程序?qū)崿F(xiàn)一下,先看類圖媒役,如圖5.1所示:
??分別定義了老師祝谚,班長,學(xué)生三個角色酣衷,Teacher通過Command方法讓Monitor檢查學(xué)生的學(xué)習(xí)情況交惯,Monitor通過CheckLearn方法檢查學(xué)生,來看下代碼實現(xiàn):
/// <summary>
/// 學(xué)生
/// </summary>
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
/// <summary>
/// 班長
/// </summary>
public class Monitor
{
public void CheckLearn(List<Student> students)
{
students.ForEach(item =>
{
//檢查有沒有好好學(xué)習(xí)
var learn = new Random(Guid.NewGuid().GetHashCode()).Next(0, 2) == 1 ? "有" : "沒有";
Console.WriteLine($"{item.Name},{learn}好好學(xué)習(xí)");
});
}
}
/// <summary>
/// 老師
/// </summary>
public class Teacher
{
public void Command(Monitor monitor)
{
//初始化學(xué)生
var list = new List<Student>
{
new Student {Id = 1, Name = "張三"},
new Student {Id = 2, Name = "李四"},
new Student {Id = 3, Name = "王五"},
new Student {Id = 4, Name = "趙六"},
new Student {Id = 5, Name = "陳七"}
};
//班長檢查學(xué)習(xí)情況
monitor.CheckLearn(list);
}
}
場景中調(diào)用:
class Program
{
static void Main(string[] args)
{
try
{
var teacher = new Teacher();
teacher.Command(new Monitor());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}
}
運行結(jié)果:
張三席爽,沒有好好學(xué)習(xí)
李四意荤,有好好學(xué)習(xí)
王五,有好好學(xué)習(xí)
趙六只锻,有好好學(xué)習(xí)
陳七玖像,沒有好好學(xué)習(xí)
?? 整個過程都實現(xiàn)了,貌似沒有什么問題齐饮,但回過頭來想想捐寥,老師有幾個直接的朋友類,這里面其實就只有一個祖驱,那就是班長握恳,直接告訴的班長。那老師也依賴了學(xué)生啊捺僻,不也是朋友類嗎乡洼?迪米特法則告訴我們,像這種方法體內(nèi)的類不屬于朋友類陵像,一個類只和朋友說話就珠,班長才直接和學(xué)生有交流。Command中出現(xiàn)List<Student>列表醒颖,會影響程序的穩(wěn)定性妻怎。同時,我們看班長檢查學(xué)習(xí)情況泞歉,只需要起檢查作用逼侦,學(xué)生學(xué)習(xí)是自己的事情,比如他要認(rèn)真聽課腰耙,好好寫作業(yè)榛丢。有沒有好好學(xué)習(xí)也要有個標(biāo)準(zhǔn),不能主觀來判斷挺庞,上面實例中處理的也比較簡單晰赞,我們來深入下,當(dāng)然选侨,也不是很深入掖鱼,再加上上面朋友類的問題,來重構(gòu)一下程序援制,修改一下類圖戏挡,如圖5.2所示:
斷開了非直接的朋友,學(xué)生的學(xué)習(xí)情況也內(nèi)聚到自身晨仑,具體看一下代碼實現(xiàn):
/// <summary>
/// 學(xué)生
/// </summary>
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
private readonly Random _random = new Random(Guid.NewGuid().GetHashCode());
private int _score; //只是內(nèi)部服務(wù)的屬性褐墅,要封閉到內(nèi)部
private const int GoodScore = 180;
public void Learn()
{
this.Lesson();
this.Homework();
var learnString = this._score > GoodScore ? "有" : "沒有";
Console.WriteLine($"{this.Name}拆檬,{learnString}好好學(xué)習(xí)");
}
/// <summary>
/// 聽課
/// 內(nèi)部方法,盡量減少公開的方法和屬性
/// </summary>
private void Lesson()
{
this._score += _random.Next(150);
}
/// <summary>
/// 寫作業(yè)
/// </summary>
private void Homework()
{
this._score += _random.Next(150);
}
}
/// <summary>
/// 班長
/// </summary>
public class Monitor
{
public List<Student> StudentList { get; set; }
public void CheckLearn()
{
StudentList.ForEach(item =>
{
//只關(guān)心調(diào)用方法妥凳,不需要知道細(xì)節(jié)
item.Learn();
});
}
}
/// <summary>
/// 老師
/// </summary>
public class Teacher
{
public void Command(Monitor monitor)
{
//斷開了對Student的依賴竟贯,減少依賴,不跟非直接的朋友說話
//班長檢查學(xué)習(xí)情況
monitor.CheckLearn();
}
}
場景中調(diào)用:
class Program
{
static void Main(string[] args)
{
try
{
//初始化學(xué)生
var studentList = new List<Student>
{
new Student {Id = 1, Name = "張三"},
new Student {Id = 2, Name = "李四"},
new Student {Id = 3, Name = "王五"},
new Student {Id = 4, Name = "趙六"},
new Student {Id = 5, Name = "陳七"}
};
var teacher = new Teacher();
teacher.Command(new Monitor {StudentList = studentList});
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}
}
六逝钥、開閉原則(Open Closed Principle澄耍,OCP)
一個軟件實體應(yīng)當(dāng)對擴(kuò)展開放,對修改封閉
?? 一個軟件或系統(tǒng)在開發(fā)的過程中晌缘,以及上線生產(chǎn)后,隨著時間的推移痢站,都會產(chǎn)生變化磷箕,這是鐵定的事實。那我們在設(shè)計時就要盡量適應(yīng)這些變化阵难,來提高系統(tǒng)的穩(wěn)定性和靈活性岳枷,開閉原則就是說一個軟件實體應(yīng)該通過擴(kuò)展來實現(xiàn)變化,而不是通過修改已有的代碼來實現(xiàn)變化呜叫。開閉原則指導(dǎo)我們?nèi)绾谓⒁粋€穩(wěn)定的空繁、靈活的系統(tǒng)
。
?? 開閉原則是最基礎(chǔ)的原則朱庆,也是最重要的面向?qū)ο笤O(shè)計原則盛泡,在面向?qū)ο蟮拈_發(fā)時,都會提到的原則娱颊。開閉原則就是一個目標(biāo)傲诵,其它五大原則都是實現(xiàn)手段,它就像一個口號箱硕,你們都要奔著這個口號來拴竹。要滿足開閉原則,就需要對系統(tǒng)進(jìn)行抽象化設(shè)計剧罩,抽象化是開閉原則的關(guān)鍵栓拜,換句說法就是對系統(tǒng)進(jìn)行抽象約束。
?? 每個人心中惠昔,都有一個武俠夢幕与,夢想到有朝一日,能成為一個俠客舰罚。如今纽门,各種武俠類游戲也層出不窮,好多人都喜歡玩营罢,畢竟有個圓夢的地方了赏陵。游戲中饼齿,有各種門派,如少林蝙搔,武當(dāng)缕溉,玩家選擇各自喜歡的門派,玩的美滋滋的吃型。我們來實現(xiàn)下這個過程证鸥,類圖如6.1所示
?? 上圖中,Player可以玩游戲了勤晚,武當(dāng)枉层、少林都能玩,可是游戲是會迭代的赐写,有一天上了一個新版本鸟蜡,加入了一個新的門派,不但要修改Player類挺邀,還要更改場景中的處理揉忘,違反了開閉原則。重構(gòu)一下端铛,如類圖6.2所示
?? 增加了一個門派接口泣矛,各門派需要實現(xiàn)接口。Player只依賴接口禾蚕,通過場景類來確定玩家玩什么門派您朽,來看下代碼實現(xiàn):
public interface IFaction
{
void Characteristic();
}
public class WuDang : IFaction
{
public void Characteristic()
{
Console.WriteLine("武當(dāng)派是攻擊系的,內(nèi)功遠(yuǎn)程群攻夕膀,很6虚倒。");
}
}
public class ShaoLin : IFaction
{
public void Characteristic()
{
Console.WriteLine("少林派是防御系的,內(nèi)功防御产舞,你們都打不疼我魂奥。");
}
}
public class Player
{
public void PlayFaction(IFaction faction)
{
Console.WriteLine($"玩家開始玩游戲,選擇的是{faction.GetType().Name}");
faction.Characteristic();
}
}
場景中調(diào)用:
class Program
{
static void Main(string[] args)
{
try
{
var player = new Player();
//玩家玩武當(dāng)
player.PlayFaction(new WuDang());
//玩家玩少森
player.PlayFaction(new ShaoLin());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}
}
很簡單易猫,運行結(jié)果就不展示了耻煤。玩什么,就調(diào)用什么准颓,如果新增一個門派峨眉哈蝇,代碼如下所示:
public class EMei : IFaction
{
public void Characteristic()
{
Console.WriteLine("峨眉派是治愈系的,醫(yī)術(shù)很高攘已,有起死回生之力炮赦。");
}
}
場景中調(diào)用:
class Program
{
static void Main(string[] args)
{
try
{
var player = new Player();
//新門派上線了,我要試試
player.PlayFaction(new EMei());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}
}
?? 看到了吧样勃,是不是很容易吠勘,加一個門派性芬,只在場景中很少的改動,就新增了一個門派剧防,都是通過擴(kuò)展來實現(xiàn)的植锉,原有的類都未進(jìn)行改動過。這就是開閉原則峭拘,對擴(kuò)展開放俊庇,對修改封閉。
?? 至此鸡挠,六大原則就說完了辉饱,寫出來還真是挺不容易的,比想象中的要困難的多拣展,如有什么錯誤或問題討論鞋囊,多多指正和交流。后面的23種設(shè)計模式才是真槍實彈瞎惫,堅持!