设计原则学习
设计原则学习
(1)单一职责原则
一个类应该只有一个发生变化的原因
当前类不符合单一职责原则:
高内聚:把模块内部相关联的元素聚集起来
当内部细节变化,需要对类的内部修改;
当整体变化(添加新功能)的时候,也需要对类的实现方法进行修改
public class TelPhone
{
public void Dial(string phone_number)
{
Console.WriteLine("给"+phone_number+"打电话");
}
public void HangUp(string phone_number)
{
Console.WriteLine("挂断" + phone_number + "打电话");
}
public void SendMessage(string message)
{
Console.WriteLine("发送" + message + "");
}
public void ReceiveMessage(string message)
{
Console.WriteLine("接受" + message + "");
}
}
现在对代码进行修改:
public interface IDial
{
void DialNumber(string phone_number);
}
public interface IHangUp
{
void HangUpNumber(string phone_number);
}
public interface ISendMessage
{
void SendMessage(string message);
}
public interface IReceiveMessage
{
void ReceiveMessage(string message);
}
public class Dial : IDial
{
public void DialNumber(string phone_number)
{
Console.WriteLine("给" + phone_number + "打电话");
}
}
public class HangUp : IHangUp
{
public void HangUpNumber(string phone_number)
{
Console.WriteLine("挂断" + phone_number + "打电话");
}
}
public class SendMessageClass : ISendMessage
{
public void SendMessage(string message)
{
Console.WriteLine("发送" + message + "");
}
}
public class ReceiveMessageClass : IReceiveMessage
{
public void ReceiveMessage(string message)
{
Console.WriteLine("接受" + message + "");
}
}
public class TelIPhone
{
private IDial _iDial;
private IHangUp _iHangup;
private ISendMessage _iSendMessage;
private IReceiveMessage _iReceiveMessage;
public void DialPhoneNumber(string phone_number)
{
_iDial.DialNumber(phone_number);
}
public void HangUpPhoneNumber(string phone_number)
{
_iHangup.HangUpNumber(phone_number);
}
public void SendMessage(string message)
{
_iSendMessage.SendMessage(message);
}
public void ReceiveMessage(string message)
{
_iReceiveMessage.ReceiveMessage(message);
}
}
现在问题:如果DialPhoneNumber()方法变动,那么需要修改TelIPhone类吗?不用,TelIPhone类仅仅是调用,而非实现。如果要修改需要Dial类的方法。
那么引起TelIPhone类变化的情况就是给当前类添加新的功能时候。
(2)开放封闭原则
软件实体(类、模块、函数等)应该可以拓展,但是不可以修改。即对拓展是开放的(open for extension),但是对更改是封闭的(closed for modification)
面向抽象/接口编程
把可能发生变化的地方使用 接口/抽象 进行封装
1.将BankProcess的执行方法 抽象为接口,然后将接口实现
public partial class Program
{
static void Main(string[] args)
{
BankClient bankClient= new BankClient();
bankClient.BankType = "存款";
BankStuff bankStuff= new BankStuff();
bankStuff.HandleProcess(bankClient);
}
}
public interface IDeposite
{
void DepositeInterface();
}
public interface IDrawMoney
{
void DrawMoneyInterface();
}
public class DepositeClass : IDeposite
{
public void DepositeInterface()
{
Console.WriteLine("存款");
}
}
public class DrawMoneyClass : IDrawMoney
{
public void DrawMoneyInterface()
{
Console.WriteLine("取款");
}
}
public class BankProcess
{
public IDeposite deposite { get; set; }
public IDrawMoney drawMoney { get; set; }
public void DepositeFunc()
{
this.deposite.DepositeInterface();
}
public void DrawMoneyFunc()
{
this.drawMoney.DrawMoneyInterface();
}
}
public class BankStuff
{
private BankProcess BankProcess = new BankProcess();
public void HandleProcess(BankClient bankClient)
{
switch (bankClient.BankType)
{
case "存款":
BankProcess.deposite = new DepositeClass();
BankProcess.DepositeFunc();
break;
default:
break;
}
}
}
public class BankClient
{
public string BankType;
}
第二次修改:
将BankProcess的处理行为抽象为接口,然后让各种行为实现接口。调用的时候,使用对应的子类来初始化对象
public partial class Program
{
static void Main(string[] args)
{
BankClient bankClient= new BankClient();
bankClient.BankType = "存款";
BankStuff bankStuff= new BankStuff();
bankStuff.HandleProcess(bankClient);
}
}
public interface IDrawMoney
{
void DrawMoneyInterface();
}
public class BankStuff
{
private IBankProcess _bankProcess ;
public void HandleProcess(BankClient bankClient)
{
switch (bankClient.BankType)
{
case "存款":
_bankProcess = new DepositeClass();
_bankProcess.BankProcess();
break;
default:
break;
}
}
}
public interface IBankProcess
{
public void BankProcess();
}
public class DepositeClass: IBankProcess
{
public void BankProcess()
{
Console.Write("存款");
}
}
public class BankClient
{
public string BankType;
}
目的:对BankClient类进行修改:
HandleProcess中的变化太多了。
封装,抽象不是目的,目的是封装变化。
只有把变化的地方进行封装,程序才能做到高内聚低耦合
public partial class Program
{
static void Main(string[] args)
{
IBankClient bankClient = new DepositeClient();
BankStuff bankStuff = new BankStuff();
bankStuff.HandleProcess(bankClient);
}
}
public interface IDrawMoney
{
void DrawMoneyInterface();
}
public class BankStuff
{
private IBankProcess _bankProcess;
public void HandleProcess(IBankClient bankClient)
{
_bankProcess = bankClient.GetBankProcess();
_bankProcess.BankProcess();
}
}
public interface IBankProcess
{
public void BankProcess();
}
public class DepositeClass : IBankProcess
{
public void BankProcess()
{
Console.Write("存款");
}
}
public class BankClient
{
public string BankType;
}
public interface IBankClient
{
// 封装的是变化
// 根据不同需求的客户端,返回不同的处理对象/处理办法
public IBankProcess GetBankProcess();
}
public class DepositeClient : IBankClient
{
public IBankProcess GetBankProcess()
{
return new DepositeClass();
}
}
(3)依赖倒置原则
依赖倒置原则(Dependence Inversion Principle,DIP)是指设计代码结构时,
高层模块不应该依赖低层模块,二者都应该依赖其抽象。
抽象不应该依赖细节,细节应该依赖抽象。
public partial class Program
{
static void Main(string[] args)
{
Singer singer = new Singer();
singer.SingSong(new ChineseSong());
}
}
public class Singer
{
public void SingSong(ISong song)
{
Console.WriteLine("歌手 唱 " + song.GetSongWords());
}
}
public interface ISong
{
public string GetSongWords();
}
public class ChineseSong: ISong
{
public string GetSongWords()
{
return "中国歌曲";
}
}
高层模式使用底层模式。所以高层模块(客户类)是Singer,底层模块(服务类)是Song。现在高层模式依赖了抽象的ISong接口,不再依赖具体的服务类,这样没办法在服务类的内部实例化一个ISong对象(SingSong方法)。所以我们在客户类提供了一个服务类的注入点ISong参数。让Program类在使用的时候,具体实例化服务类注入到客户类中。
依赖注入的方法:
-
接口注入
-
构造函数注入
-
属性的set注入
(4)里式替换原则
子类可以扩展父类的功能,但不能改变父类原有的功能
(5)接口隔离原则
使用方不应该依赖于它不使用的方法
简单来说:把接口变小,把职责细分。(用于实现低耦合)
不要都实现了大接口中的方法,这样生成的类耦合性会很高
(6)迪米特原则
迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。
比如关机操作,外部类只要调用Computer的关机方法就行了。没必要感知关机行为中的具体方法
public class Person
{
public void CloseComputer(Computer computer)
{
computer.CloseComputer();
}
}
public class Computer
{
private void SaveCurrentTask()
{
}
private void CloseScreen()
{
}
private void ShutDown()
{
}
public void CloseComputer()
{
this.SaveCurrentTask();
this.CloseScreen();
this.ShutDown();
}
}
只和自己的直接朋友通信
- 成员对象
- 方法参数
- 方法返回值
- 注意:局部变量中的类,不是直接朋友
public partial class Program
{
static void Main(string[] args)
{
BranchOfficeManager branchOfficeManager = new BranchOfficeManager();
new HeadOfficeManager().Print(branchOfficeManager);
}
}
public class HeadOfficeEmployee
{
public int Id { get; set; }
}
public class BranchOfficeEmployee
{
public int Id { get; set; }
}
/// <summary>
/// 总公司管理者
/// </summary>
public class HeadOfficeManager
{
/// <summary>
/// 获取员工数
/// </summary>
/// <returns></returns>
public List<HeadOfficeEmployee> GetEmployees()
{
List<HeadOfficeEmployee> headOffices = new List<HeadOfficeEmployee>();
for (int i = 0; i < 10; i++)
{
HeadOfficeEmployee headOfficeEmployee = new HeadOfficeEmployee();
headOfficeEmployee.Id = i;
headOffices.Add(headOfficeEmployee);
}
return headOffices;
}
/// <summary>
/// 打印总公司员工id
/// </summary>
public void Print(BranchOfficeManager branchOfficeManager)
{
var datas = GetEmployees();
Console.WriteLine("总公司员工Id分别是:");
foreach (var item in datas)
{
Console.WriteLine(+item.Id);
}
//branchOfficeEmployes这个集合对象,是通过局部变量的形式出现在类中的,所以不是我们的直接朋友,故不符合迪米特原则。
var branchOfficeEmployes = branchOfficeManager.GetBranchOfficeEmployees();
Console.WriteLine("分公司员工Id分别是:");
foreach (var item in branchOfficeEmployes)
{
Console.WriteLine(+item.Id);
}
}
}
public class BranchOfficeManager
{
public List<BranchOfficeEmployee> GetBranchOfficeEmployees()
{
List<BranchOfficeEmployee> list= new List<BranchOfficeEmployee>();
for (int i = 0; i < 5; i++)
{
BranchOfficeEmployee branchdOfficeEmployee = new BranchOfficeEmployee();
branchdOfficeEmployee.Id=i;
list.Add(branchdOfficeEmployee);
}
return list;
}
}
修改以后:
public partial class Program
{
static void Main(string[] args)
{
new BranchOfficeManager().Print();
new HeadOfficeManager().Print();
}
}
public class HeadOfficeEmployee
{
public int Id { get; set; }
}
public class BranchOfficeEmployee
{
public int Id { get; set; }
}
public class BranchOfficeManager
{
public List<BranchOfficeEmployee> GetBranchOfficeEmployees()
{
List<BranchOfficeEmployee> list = new List<BranchOfficeEmployee>();
for (int i = 0; i < 5; i++)
{
BranchOfficeEmployee branchdOfficeEmployee = new BranchOfficeEmployee();
branchdOfficeEmployee.Id = i;
list.Add(branchdOfficeEmployee);
}
return list;
}
public void Print()
{
var datas = GetBranchOfficeEmployees();
Console.WriteLine("分公司员工Id分别是:");
foreach (var item in datas)
{
Console.WriteLine(+item.Id);
}
}
}
/// <summary>
/// 总公司管理者
/// </summary>
public class HeadOfficeManager
{
/// <summary>
/// 获取员工数
/// </summary>
/// <returns></returns>
public List<HeadOfficeEmployee> GetEmployees()
{
List<HeadOfficeEmployee> headOffices = new List<HeadOfficeEmployee>();
for (int i = 0; i < 10; i++)
{
HeadOfficeEmployee headOfficeEmployee = new HeadOfficeEmployee();
headOfficeEmployee.Id = i;
headOffices.Add(headOfficeEmployee);
}
return headOffices;
}
/// <summary>
/// 打印总公司员工id
/// </summary>
public void Print()
{
var datas = GetEmployees();
Console.WriteLine("总公司员工Id分别是:");
foreach (var item in datas)
{
Console.WriteLine(+item.Id);
}
}
}
(7)合成复用原则
- 定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。
- 聚合has-A和组合contains-A。
- 优点:可以使系统更加灵活,降低类与类之间的耦合度,一个雷的变化对其他类造成的影响相对较少。
把已经有的对象,纳入新对象中,作为新对象的对象成员来实现的,新对象可以调用已有对象的功能,从而达到复用。
public partial class Program
{
static void Main(string[] args)
{
Car qyCar = new QYCar();
qyCar.Move(new Red());
}
}
public interface IColor
{
string ShowColor();
}
public class Red : IColor
{
public string ShowColor()
{
return "红色";
}
}
public abstract class Car
{
public abstract void Move(IColor color);
}
public class QYCar : Car
{
public override void Move(IColor color)
{
Console.WriteLine(color.ShowColor() + "颜色的汽车在行驶");
}
}