单一指责原则(Single Responsibility Principle) SRP
using System; using System.Collections.Generic; using System.Text; namespace SingleResponsibilityPrinciple { //单一指责原则(Single Responsibility Principle) SRP //There should never be more than one reason for a class to change. //有且只有一个原因引起类的变更。 class Program { //使用场景 static void Main(string[] args) { //职责单一适用于接口,类,方法。 //让我们来先看接口是如何体现职责单一的。 //我们都知道自从IPHONE使用USB_Type_C接口后,许多厂家也真先恐后的加入了使用该接口的行列。 //新MacBook的USB_Type_C接口能够传输数据、进行充电也可以作为视频输出端口链接外部显示设备。 //很明显,违反了职责单一的原则!接口包含的三个功能根本毫不相关,如果用户同时要充电,要传输数据,要接显示器,怎么办?而苹果为了减轻MacBook的厚度和重量,为此做了权衡。 //这是产品设计上的取舍,你也不能说不对,各有利弊,对于软件开发来说,也是一样的。 //例如模拟用户使用USB_Type_C接口给手机充电。 Iphone6 iphone = new Iphone6(); XiaoMiM5 xiaomi = new XiaoMiM5(); Charger_C(iphone); Charger_C(xiaomi); //而如今闪充技术现在已经不是什么新闻了,我们假设Iphone等不住了要升级充电功能到闪充,不然用户不买账啊。 //想想会发生什么事情?USB_Type_C有3个原因(充电,数据传输,显示)会引起类的变更,由于iphone实现了USB_Type_C接口,想要升级,就肯定先要USB_Type_C接口声明闪充功能。 //这个时候小米和其他使用了USB_Type_C的涉及到充电的厂商不干了啊,USB_Type_C升级闪充,我们也要跟着变了啊(子类必须实现接口声明方法,由于只是内部充电方法改变,接口没变,所以传输和显示相关设备厂商不用改变,充电设备厂商需要适配新的方法),增加了成本和风险,价格不发烧了呀,用户不买账啊。 //什么?你说新建一个Flash_USB_Type_C接口?让苹果自己玩去?传输和显示功能没变。就因为闪充而新增一个新的接口Flash_USB_Type_C,那以前苹果涉及到USB_Type_C接口的相关代码不是全部要适配新的Flash_USB_Type_C接口? //而且你还要叫其他iphone辅助设备厂家来适配新接口(包括传输和显示相关设备厂商,因为接口已经变了,但其实内部的传输与显示方法并没改变,造成不同接口,声明了实际相同的功能,变复杂了),苹果也不干了(虽然以前苹果产品的接口都是特立独行的……)。 //撕逼大战从此开始,谁叫你们当初选择跟风使用USB_Type_C接口的,说好的职责单一呢?魅族和华为表示毫无压力,因为它分别实现了不同功能的接口。 MeiZu mz = new MeiZu(); Charger_N(mz); HuaWei hw = new HuaWei(); Charger_N(hw); //魅族想升级就升级,IFlashCharge闪充是单独的充电功能模块,并且业界已经成熟,魅族替换掉该充电接口即可,华为当然不受影响,依然用着以前的充电技术ICharger~ FlashMeiZu fmz = new FlashMeiZu(); FlashCharger(fmz); //传输,显示技术的更新换代,原理也一样的,如果不符合职责单一,大家一起折腾去吧。所以接口的职责单一,主要体现在变化上。 //对于方法的职责单一,也很好理解,例如一个用户拨打电话的场景 Iphone6 ip6 = new Iphone6(); //请按F12进入函数看详情 ip6.CallPeople("1307779****"); //符合职责单一方法,你也许会想,SRPCallPeople方法本身好像职责就不单一啊,用了那么多方法?这个需要在不同层面考虑的。 //SRPCallPeople已经是业务逻辑处理函数方法,并只不是功能了,肯定会调用其他方法实现某种业务。而方法的职责单一,主要体现在功能上。 ip6.SRPCallPeople("1307779****"); //最后对于类的职责单一,很大程度上与方法类似。 //例如各种类库,每个类库都实现自己的相关功能(见项目-引用),如果不按照职责单一,不用命名空间区分,它们完全可以在一个类文件中存在啊,简直就是代码混沌世界! //所以一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作 //因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。 //职责单一,也就是让我们多多考虑高内聚、低耦合 } //PS.面向接口编程,只关注行为规范,管他对象类是谁。低耦合 //我是一个普通的充电器 static void Charger_N(ICharge device) { device.Charge(); } //我是一个支持USB_Type_C的充电器~(Flash_USB_Type_C这种接口的充电器还不存在) static void Charger_C(IUSB_Type_C device) { device.Charge(); } //充电5分钟,通话2小时(闪充充电器已经成熟,很多厂家都开始生产) static void FlashCharger(IFlashCharge device) { device.FlashCharge(); } } public class Iphone6 : IUSB_Type_C { //打电话给某人 public void CallPeople(string number) { //发现问题木有?拨打电话的方法全部在这个函数实现了,说好的职责单一呢? //想想看,代码长度很长的时候,看起来是不是很晕,你很可能不知道这些不同的代码到底是干什么用的,很容易引入BUG。 //并且,检查电话,拨打电话,显示运营商,这些功能并不是只在这里实现。 //所以最好提取出来,做到“方法“的单一,不仅好理解,其他地方也可以调用,减少冗余,复用代码,提高效率~ Console.WriteLine("检查电话号码是否合法"); if (number.Length == 11) { Console.WriteLine("拨打电话"); } if (number.Substring(0, 3).Contains("130")) { Console.WriteLine("显示电话号码运营商,中国联通"); } } //符合职责单一的拨打电话方法哟 public void SRPCallPeople(string number) { CheckNumber(number); Call(number); ShowOperator(number); } private bool CheckNumber(string number) { Console.WriteLine("单一检查电话号码是否合法"); if (number.Length > 11) { return true; } else { return false; } } private void Call(string number) { Console.WriteLine("单一拨打电话"); } private void ShowOperator(string number) { if (number.Substring(0, 3).Contains("130")) { Console.WriteLine("单一显示电话号码运营商,中国联通"); } } public void Hangup() { Console.WriteLine("通话结束"); } public void TranData() { throw new NotImplementedException(); } public void Charge() { Console.WriteLine("Iphone6充电ing"); } public void Display() { throw new NotImplementedException(); } } public class XiaoMiM5 : IUSB_Type_C { public void TranData() { throw new NotImplementedException(); } public void Charge() { Console.WriteLine("小米M5充电ing"); } public void Display() { throw new NotImplementedException(); } } public class MeiZu : ITranData, ICharge, IDisplay { public void TranData() { throw new NotImplementedException(); } public void Charge() { Console.WriteLine("魅族充电ing"); } public void Display() { throw new NotImplementedException(); } } public class HuaWei : ITranData, ICharge, IDisplay { public void TranData() { throw new NotImplementedException(); } public void Charge() { Console.WriteLine("华为充电ing"); } public void Display() { throw new NotImplementedException(); } } public class FlashMeiZu : IFlashCharge, ITranData, IDisplay { public void TranData() { throw new NotImplementedException(); } public void Display() { throw new NotImplementedException(); } public void FlashCharge() { Console.WriteLine("小米闪充,充电5分钟,通话2小时!~"); } } public interface IUSB_Type_C { void TranData(); void Charge(); void Display(); } public interface ITranData { void TranData(); } public interface ICharge { void Charge(); } public interface IFlashCharge { void FlashCharge(); } public interface IDisplay { void Display(); } }
我不怕千万人阻挡,只怕自己投降。