一、单一职责原则
(1)核心思想:一个类最好只做一件事,只有一个引起它变化的原因
(2)常用模式:Facade模式、Proxy模式
(3)基本方法:Extract Interface 抽取接口、Extract Class 抽取类、Extract Method 抽取方法
(4)DEMO:数据库管理系统中根据不同权限进行CRUD操作(这里是使用Proxy模式重构后的代码)
public interface IDBAction { void Add(); bool Delete(); void View(); } public class DBManager : IDBAction { public DBManager(string id) { } public void Add() { //执行数据增加 Console.WriteLine("增加数据成功。"); } public bool Delete() { //执行数据删除 return true; } public void View() { //执行数据查看 } } public class DBManagerProxy : IDBAction { private string id; private IDBAction dbManager; public DBManagerProxy(IDBAction dbAction) { dbManager = dbAction; } //处理权限判断的逻辑 public string GetPermission(string id) { return null; //return "CanAdd"; } public void Add() { if (GetPermission(id) == "CanAdd") { dbManager.Add(); } } public bool Delete() { if (GetPermission(id) == "CanDelete") { dbManager.Delete(); } return true; } public void View() { if (GetPermission(id) == "View") { dbManager.View(); } } } public class DBClient { public static void Main() { IDBAction DBManager = new DBManagerProxy(new DBManager("CanAdd")); DBManager.Add(); } }
(5)规则建议:
①一个类只有一个引起它变化的原因,否则就应当考虑重构;
②测试驱动开发,有助于实现合理分离功能的设计;
③可以通过Facade模式或Proxy模式进行职责分离;
二、开放封闭原则
(1)核心思想:软件实体应该是可扩展的,而不可修改的-->即对扩展开放,对修改封闭。在面向对象的编程中,即对抽象编程,而不对具体编程。
(2)常用模式:Template Method模式、Strategy模式
(3)DEMO:银行窗口业务办理场景
class Client { private string ClientType; public Client(string clientType) { ClientType = clientType; } public IBankProcess CreateProcess() { //实际的处理 switch (ClientType) { case "存款用户": return new DepositProcess(); break; case "转帐用户": return new TransferProcess(); break; case "取款用户": return new DrawMoneyProcess(); break; } return null; } } interface IBankProcess { void Process(); } //按银行按业务进行分类 class DepositProcess : IBankProcess { public void Process() { //办理存款业务 Console.WriteLine("处理存款。"); } } class TransferProcess : IBankProcess { public void Process() { //办理转帐业务 Console.WriteLine("处理转帐。"); } } class DrawMoneyProcess : IBankProcess { public void Process() { //办理取款业务 } } class FundProcess : IBankProcess { public void Process() { //办理基金业务 } } class EasyBankStaff { private IBankProcess bankProc = null; public void HandleProcess(Client client) { //业务处理 bankProc = client.CreateProcess(); bankProc.Process(); } } class BankProcess { public static void Main() { EasyBankStaff bankStaff = new EasyBankStaff(); bankStaff.HandleProcess(new Client("转帐用户")); } }
(4)规则建议:
①Liskov替换原则和合成/聚合复用原则为OCP实现提供保证;
②可以通过Template Method和Strategy模式进行重构;
③封装变化是实现OCP的重要手段;
三、依赖倒置原则
(1)核心思想:依赖于抽象-->抽象不应该依赖于具体,具体应该依赖于抽象;
(2)基本方法:在依赖之间定义一个抽象的接口,高层模块调用接口的方法,低层模块实现接口的定义;
(3)DEMO:银行窗口不同业务客户场景
interface IClient { IBankProcess CreateProcess(); } class DepositClient : IClient { IBankProcess IClient.CreateProcess() { return new DepositProcess(); } } class TransferClient : IClient { IBankProcess IClient.CreateProcess() { return new TransferProcess(); } } class DrawMoneyClient : IClient { IBankProcess IClient.CreateProcess() { return new DrawMoneyProcess(); } } class FundClient : IClient { IBankProcess IClient.CreateProcess() { return new FundProcess(); } } interface IBankProcess { void Process(); } //按银行按业务进行分类 class DepositProcess : IBankProcess { public void Process() { //办理存款业务 Console.WriteLine("处理存款。"); } } class TransferProcess : IBankProcess { public void Process() { //办理转帐业务 Console.WriteLine("处理转帐。"); } } class DrawMoneyProcess : IBankProcess { public void Process() { //办理取款业务 } } class FundProcess : IBankProcess { public void Process() { //办理基金业务 } } class EasyBankStaff { private IBankProcess bankProc = null; public void HandleProcess(IClient client) { //业务处理 bankProc = client.CreateProcess(); bankProc.Process(); } } class BankProcess { public static void Main()//Main_2_4_1 { EasyBankStaff bankStaff = new EasyBankStaff(); bankStaff.HandleProcess(new TransferClient()); } }
(4)规则建议:
①必须权衡在抽象和具体之间的取舍,方法不是一成不变的;
②依赖于抽象就是要对接口编程,不要对实现编程;
四、接口隔离原则
(1)核心思想:使用多个小的专门的接口,而不使用一个大的总接口;接口应该是内聚的,应该避免出现“胖”接口;不要强迫依赖不用的方法,这是一种接口污染;
(2)基本方法:委托分离与多重继承分离(推荐)
(3)DEMO:不同年龄段人士使用电脑场景
interface IComputerLearn { void ToLearn(); } interface IComputerWork { void ToWork(); } interface IComputerBeFun { void ToBeFun(); } class Adult { private IComputerLearn myLearn; private IComputerWork myWork; private IComputerBeFun myFun; public void UseComputer() { //主要是工作 myWork.ToWork(); //还可以娱乐 myFun.ToBeFun(); } } class Child { private IComputerLearn myLearn; public void UseComputer() { //只有学习,不会依赖其他的方法 myLearn.ToLearn(); } }
(4)规则建议:
①将功能相近的接口合并,可能造成接口污染;
②接口隔离能够保证系统扩展和修改的影响不会扩展到系统其他部分;
五、Liskov替换原则
(1)核心思想:子类必须能够替换其基类
(2)DEMO:父类提供虚函数,子类覆写虚函数
class FatherClass { public virtual void Method() { //父类的行为 Console.WriteLine("Father Method."); } } class SonClass : FatherClass { public override void Method() { //子类的行为 Console.WriteLine("Son Method."); } } class GrandsonClass : SonClass { public override void Method() { Console.WriteLine("Grandson Method."); } } class Test_LSP { public static void DoSomething(FatherClass f) { f.Method(); } public static void Main_2_6_2()//Main_2_6_2 { DoSomething(new SonClass()); SonClass son = new SonClass(); FatherClass father = son is FatherClass ? (FatherClass)son : null; father.Method(); FatherClass f2 = new FatherClass(); SonClass son2 = f2 is SonClass ? (SonClass)f2 : null; son2.Method(); } }
(3)规则建议:
①违反了Liskov替换原则必然导致违反开放封闭原则;
②Liskov替换能够保证系统具有良好的扩展性;
③之类的异常必须控制在父类可以预计的范围,否则将导致替换违规;
本章思维导图