设计原则学习
Posted 聆听微风
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计原则学习相关的知识,希望对你有一定的参考价值。
设计原则学习
(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() + "颜色的汽车在行驶");
设计模式学习笔记 单一职责原则的判定与设计
1. SOLID原则
SOLID原则由5个原则组成,他们分别是:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖反转原则。
2. 单一职责原则
单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。这个原则的英文描述是这样的:A class or module should have a single reponsibility。如果我们把它翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)。
在单一职责原则中,一个类只负责完成一个职责或者功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。
3. 如何判断类的职责是否足够单一?
不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的。在某种应用场景或者当下的需求背景下,一个类的设计可能已经满足单一职责原则了,但如果换个应用场景或着在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。
-
类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
-
类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
-
私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
-
比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
-
类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来。
-
代码行数在50~70行左右,属性在10个以内是比较合适的
4. 单一职责原则的优缺点
类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。
以上是关于设计原则学习的主要内容,如果未能解决你的问题,请参考以下文章