asp.net core系列 65 正反案例介绍SOLID原则

  SOLID五大原则使咱们可以管理解决大多数软件设计问题。由Robert C. Martin在20世纪90年代编写了这些原则。这些原则为咱们提供了从紧耦合的代码和少许封装转变为适当松耦合和封装业务实际需求的结果方法。使用这些原则,咱们能够构建一个具备整洁,可读且易于维护的代码应用程序。
  
  SOLID缩写以下:
  
  SRP  单一责任原则
  
  OCP 开放/封闭原则
  
  LSP  里氏替换原则
  
  ISP   接口分离原则
  
  DIP   依赖反转原则
  
  1.单一责任原则SRP
  
  一个类承担的责任在理想状况下应该是多少个呢?答案是一个。这个责任是围绕一个核心任务构建,不是简化的意思。经过暴露很是有限的责任使这个类与系统的交集更小。
  
  (1) 演示:违反了单一责任原则,缘由是:顾客类中承担了太多无关的责任。 
  
  复制代码
  
  /// <summary>
  
  /// 顾客类全部实现
  
  /// </summary>
  
  public class Cliente
  
  {
  
  public int ClienteId { get; set; }
  
  public string Nome { get; set; }
  
  public string Email { get; set; }
  
  public string CPF { get; set; }
  
  public DateTime DataCadastro { get; set; }
  
  public string AdicionarCliente()
  
  {
  
  //顾客信息验证
  
  if (!Email.Contains("@"))
  
  return "Cliente com e-mail inválido";
  
  if (CPF.Length != 11)
  
  return "Cliente com CPF inválido";
  
  //保存顾客信息
  
  using (var cn = new SqlConnection())
  
  {
  
  var cmd = new SqlCommand();
  
  cn.ConnectionString = "MinhaConnectionString";
  
  cmd.Connection = cn;
  
  cmd.CommandType = CommandType.Text;
  
  cmd.CommandText = "INSERT INTO CLIENTE (NOME, EMAIL CPF, DATACADASTRO) VALUES (@nome, @email, @cpf, @dataCad))";
  
  cmd.Parameters.AddWithValue("nome", Nome);
  
  cmd.Parameters.AddWithValue("email", Email);
  
  cmd.Parameters.AddWithValue("cpf", CPF);
  
  cmd.Parameters.AddWithValue("dataCad", DataCadastro);
  
  cn.Open();
  
  cmd.ExecuteNonQuery();
  
  }
  
  //发布邮件
  
  var mail = new MailMessage("empresa@empresa.com", Email);
  
  var client = new SmtpClient
  
  {
  
  Port = 25,
  
  DeliveryMethod = SmtpDeliveryMethod.Network,
  
  UseDefaultCredentials = false,
  
  Host = "smtp.google.com"
  
  };
  
  mail.Subject = "Bem Vindo.";
  
  mail.Body = "Parabéns! Você está cadastrado.";
  
  client.Send(mail);
  
  return "Cliente cadastrado com sucesso!";
  
  }
  
  }
  
  复制代码
  
      (2) 解决方案,使用单一责任原则,每一个类只负责本身的业务。
  
  复制代码
  
  /// <summary>
  
  /// 顾客实体
  
  /// </summary>
  
  public class Cliente
  
  {
  
  public int ClienteId { get; set; }
  
  public string Nome { get; set; }
  
  public string Email { get; set; }
  
  public string CPF { get; set; }
  
  public DateTime DataCadastro { get; set; }
  
  /// <summary>
  
  /// 顾客信息验证
  
  /// </summary>
  
  /// <returns></returns>
  
  public bool IsValid()
  
  {
  
  return EmailServices.IsValid(Email) && CPFServices.IsValid(CPF);
  
  }
  
  }
  
  /// <summary>
  
  /// 保存顾客信息
  
  /// </summary>
  
  public class ClienteRepository
  
  {
  
  /// <summary>
  
  /// 保存
  
  /// </summary>
  
  /// <param name="cliente">要保存的顾客实体</param>
  
  public void AdicionarCliente(Cliente cliente)
  
  {
  
  using (var cn = new SqlConnection())
  
  {
  
  var cmd = new SqlCommand();
  
  cn.ConnectionString = "MinhaConnectionString";
  
  cmd.Connection = cn;
  
  cmd.CommandType = CommandType.Text;
  
  cmd.CommandText = "INSERT INTO CLIENTE (NOME, EMAIL CPF, DATACADASTRO) VALUES (@nome, @email, @cpf, @dataCad))";
  
  cmd.Parameters.AddWithValue("nome", cliente.Nome);
  
  cmd.Parameters.AddWithValue("email"www.xudongqian.cn, cliente.Email);
  
  cmd.Parameters.AddWithValue("cpf"www.zykuaican.com, cliente.CPF);
  
  cmd.Parameters.AddWithValue("dataCad", cliente.DataCadastro);
  
  cn.Open();
  
  cmd.ExecuteNonQuery();
  
  }
  
  }
  
  }
  
  /// <summary>
  
  /// CPF服务
  
  /// </summary>
  
  public static class CPFServices
  
  {
  
  public static bool IsValid(string cpf)
  
  {
  
  return cpf.Length == 11;
  
  }
  
  }
  
  /// <summary>
  
  /// 邮件服务
  
  /// </summary>
  
  public static class EmailServices
  
  {
  
  public static bool IsValid(string email)
  
  {
  
  return email.Contains("@");
  
  }
  
  public static void Enviar(string de, string para, string assunto, string mensagem)
  
  {
  
  var mail = new MailMessage(de, para);
  
  var client = new SmtpClient
  
  {
  
  Port = 25,
  
  DeliveryMethod = SmtpDeliveryMethod.Network,
  
  UseDefaultCredentials = false,
  
  Host = "smtp.google.com"
  
  };
  
  mail.Subject = assunto;
  
  mail.Body =www.fdhhjyo.com mensagem;
  
  client.Send(mail);
  
  }
  
  }
  
  /// <summary>
  
  /// 客户服务,程序调用入口
  
  /// </summary>
  
  public class ClienteService
  
  {
  
  public string AdicionarCliente(Cliente cliente)
  
  {
  
  //先验证
  
  if (!cliente.IsValid())
  
  return "Dados inválidos";
  
  //保存顾客
  
  var repo = new ClienteRepository();
  
  repo.AdicionarCliente(cliente);
  
  //邮件发送
  
  EmailServices.Enviar("empresa@empresa.com", cliente.Email, "Bem Vindo", "Parabéns está Cadastrado");
  
  return "Cliente cadastrado www.shengrenyp.cn com sucesso";
  
  }
  
  }
  
  复制代码
  
  2. 开放/封闭原则OCP
  
  类应该是能够可扩展的,能够用做构建其余相关新功能,这叫开放。但在实现相关功能时,不该该修改现有代码(由于已通过单元测试运行正常)这叫封闭。
  
  (1) 演示:违反了开放/封闭原则,缘由是每次增长新形状时,须要改变AreaCalculator 类的TotalArea方法,例如开发后期又增长了圆形形状。
  
  复制代码
  
  /// <summary>
  
  /// 长方形实体
  
  /// </summary>
  
  public class Rectangle
  
  {
  
  public double Height { get; set; }
  
  public double Width { get; set; }
  
  }
  
  /// <summary>
  
  /// 圆形
  
  /// </summary>
  
  public class Circle
  
  {
  
  /// <summary>
  
  /// 半径
  
  /// </summary>
  
  public double Radius {www.chengmingyuLe.com   get; set; }
  
  }
  
  /// <summary>
  
  /// 面积计算
  
  /// </summary>
  
  public class AreaCalculator
  
  {
  
  public double TotalArea(object[] arrObjects)
  
  {
  
  double area = 0;
  
  Rectangle objRectangle;
  
  Circle objCircle;
  
  foreach (var obj in arrObjects)
  
  {
  
  if (obj is Rectangle)
  
  {
  
  objRectangle = (Rectangle)www.baichuangyule.cn obj;
  
  area += objRectangle.Height * objRectangle.Width;
  
  }
  
  else
  
  {
  
  objCircle = (Circle)obj;
  
  area += objCircle.Radius * objCircle.Radius * Math.PI;
  
  }
  
  }
  
  return area;
  
  }
  
  }
  
  复制代码
  
      (2) 解决方案,使用开放/封闭原则,每次增长新形状时(开放),不须要修改TotalArea方法(封闭)
  
  复制代码
  
    /// <summary>
  
  /// 形状抽象类
  
  /// </summary>
  
  public abstract class Shape
  
  {
  
  /// <summary>
  
  /// 面积计算
  
  /// </summary>
  
  /// <returns></returns>
  
  public abstract double Area(www.hongchenyul.cn);
  
  }
  
  /// <summary>
  
  /// 长方形
  
  /// </summary>
  
  public class Rectangle : Shape
  
  {
  
  public double Height { get; set; }
  
  public double Width { get; set; }
  
  public override double Area()
  
  {
  
  return Height * Width;
  
  }
  
  }
  
  /// <summary>
  
  /// 圆形
  
  /// </summary>
  
  public class Circle : Shape
  
  {
  
  public double Radius { get; set; }
  
  public override double Area()
  
  {
  
  return Radius * Radius * Math.PI;
  
  }
  
  }
  
  /// <summary>
  
  /// 面积计算
  
  /// </summary>
  
  public class AreaCalculator
  
  {
  
  public double TotalArea(Shape[] arrShapes)
  
  {
  
  double area = 0;
  
  foreach (var objShape in arrShapes)
  
  {
  
  area += objShape.Area();
  
  }
  
  return area;
  
  }
  
  }
  
  复制代码
  
  3.里氏替换原则LSP
  
  这里也涉及到了类的继承,也适用于接口。子类能够替换它们的父类。里氏替换原则常见的代码问题是使用虚方法,在父类定义虚方法时,要确保该方法里没有任何私有成员。
  
  (1) 演示:违反了里氏替换原则, 缘由是不能使用ReadOnlySqlFile子类替代SqlFile父类。
  
  复制代码
  
  /// <summary>
  
  /// sql文件类 读取、保存
  
  /// </summary>
  
  public class SqlFile
  
  {
  
  public string FilePath { get; set; }
  
  public string FileText { get; set; }
  
  public virtual string LoadText()
  
  {
  
  /* Code to read text from sql file */
  
  return "..";
  
  }
  
  public virtual void SaveText()
  
  {
  
  /* Code to save text into sql file */
  
  }
  
  }
  
  /// <summary>
  
  /// 开发途中增长了sql文件只读类
  
  /// </summary>
  
  public class ReadOnlySqlFile : SqlFile
  
  {
  
  public override string LoadText()
  
  {
  
  /* Code to read text from sql file */
  
  return "..";
  
  }
  
  public override void SaveText()
  
  {
  
  /* Throw an exception when app flow tries to do save. */
  
  throw new IOException("Can't Save");
  
  }
  
  }
  
  public class SqlFileManager
  
  {
  
  /// <summary>
  
  /// 集合中存在两种类:SqlFile和ReadOnlySqlFile
  
  /// </summary>
  
  public List<SqlFile> lstSqlFiles { get; set; }
  
  /// <summary>
  
  /// 读取
  
  /// </summary>
  
  /// <returns></returns>
  
  public string GetTextFromFiles()
  
  {
  
  StringBuilder objStrBuilder = new StringBuilder();
  
  foreach (var objFile in lstSqlFiles)
  
  {
  
  objStrBuilder.Append(objFile.LoadText());
  
  }
  
  return objStrBuilder.ToString();
  
  }
  
  /// <summary>
  
  /// 保存
  
  /// </summary>
  
  public void SaveTextIntoFiles()
  
  {
  
  foreach (var objFile in lstSqlFiles)
  
  {
  
  //检查当前对象是ReadOnlySqlFile类,跳过调用SaveText()方法
  
  if (!(objFile is ReadOnlySqlFile))
  
  {
  
  objFile.SaveText();
  
  }
  
  }
  
  }
  
  }
  
  复制代码
  
      (2) 解决方案,使用里氏替换原则,子类能够彻底代替父类
  
  复制代码
  
    public interface IReadableSqlFile
  
  {
  
  string LoadText();
  
  }
  
  public interface IWritableSqlFile
  
  {
  
  void SaveText();
  
  }
  
  public class ReadOnlySqlFile : IReadableSqlFile
  
  {
  
  public string FilePath { get; set; }
  
  public string FileText { get; set; }
  
  public string LoadText()
  
  {
  
  /* Code to read text from sql file */
  
  return "";
  
  }
  
  }
  
  public class SqlFile : IWritableSqlFile, IReadableSqlFile
  
  {
  
  public string FilePath { get; set; }
  
  public string FileText { get; set; }
  
  public string LoadText()
  
  {
  
  /* Code to read text from sql file */
  
  return "";
  
  }
  
  public void SaveText()
  
  {
  
  /* Code to save text into sql file */
  
  }
  
  }
  
  public class SqlFileManager
  
  {
  
  public string GetTextFromFiles(List<IReadableSqlFile> aLstReadableFiles)
  
  {
  
  StringBuilder objStrBuilder = new StringBuilder();
  
  foreach (var objFile in aLstReadableFiles)
  
  {
  
  //ReadOnlySqlFile的LoadText实现
  
  objStrBuilder.Append(objFile.LoadText());
  
  }
  
  return objStrBuilder.ToString();
  
  }
  
  public void SaveTextIntoFiles(List<IWritableSqlFile> aLstWritableFiles)
  
  {
  
  foreach (var objFile in aLstWritableFiles)
  
  {
  
  //SqlFile的SaveText实现
  
  objFile.SaveText();
  
  }
  
  }
  
  }
  
  复制代码
  
  4.接口分离原则ISP
  
  接口分离原则是解决接口臃肿的问题,建议接口保持最低限度的函数。永远不该该强迫客户端依赖于它们不用的接口。
  
  (1)  演示:违反了接口分离原则。缘由是Manager没法处理任务,同时没有人能够将任务分配给Manager,所以WorkOnTask方法不该该在Manager类中。
  
  复制代码
  
  /// <summary>
  
  /// 领导接口
  
  /// </summary>
  
  public interface ILead
  
  {
  
  //建立任务
  
  void CreateSubTask();
  
  //分配任务
  
  void AssginTask();
  
  //处理指定任务
  
  void WorkOnTask();
  
  }
  
  /// <summary>
  
  /// 团队领导
  
  /// </summary>
  
  public class TeamLead : ILead
  
  {
  
  public void AssginTask()
  
  {
  
  //Code to assign a task.
  
  }
  
  public void CreateSubTask()
  
  {
  
  //Code to create a sub task
  
  }
  
  public void WorkOnTask()
  
  {
  
  //Code to implement perform assigned task.
  
  }
  
  }
  
  /// <summary>
  
  /// 管理者
  
  /// </summary>
  
  public class Manager : ILead
  
  {
  
  public void AssginTask()
  
  {
  
  //Code to assign a task.
  
  }
  
  public void CreateSubTask()
  
  {
  
  //Code to create a sub task.
  
  }
  
  public void WorkOnTask()
  
  {
  
  throw new Exception("Manager can't work on Task");
  
  }
  
  }
  
  复制代码
  
      (2) 解决方案,使用接口分离原则
  
  复制代码
  
  /// <summary>
  
  /// 程序员角色
  
  /// </summary>
  
  public interface IProgrammer
  
  {
  
  void WorkOnTask();
  
  }
  
  /// <summary>
  
  /// 领导角色
  
  /// </summary>
  
  public interface ILead
  
  {
  
  void AssignTask();
  
  void CreateSubTask();
  
  }
  
  /// <summary>
  
  /// 程序员:执行任务
  
  /// </summary>
  
  public class Programmer : IProgrammer
  
  {
  
  public void WorkOnTask()
  
  {
  
  //code to implement to work on the Task.
  
  }
  
  }
  
  /// <summary>
  
  /// 管理者:能够建立任务、分配任务
  
  /// </summary>
  
  public class Manager : ILead
  
  {
  
  public void AssignTask()
  
  {
  
  //Code to assign a Task
  
  }
  
  public void CreateSubTask()
  
  {
  
  //Code to create a sub taks from a task.
  
  }
  
  }
  
  /// <summary>
  
  /// 团队领域:能够建立任务、分配任务、执行执行
  
  /// </summary>
  
  public class TeamLead : IProgrammer, ILead
  
  {
  
  public void AssignTask()
  
  {
  
  //Code to assign a Task
  
  }
  
  public void CreateSubTask()
  
  {
  
  //Code to create a sub task from a task.
  
  }
  
  public void WorkOnTask()
  
  {
  
  //code to implement to work on the Task.
  
  }
  
  }
  
  复制代码
  
  5. 依赖反转原则DIP
  
  依赖反转原则是对程序的解耦。高级模块/类不该依赖于低级模块/类,二者都应该依赖于抽象。意思是:当某个类被外部依赖时,就须要把该类抽象成一个接口。接口如何变成可调用的实例呢?实践中多用依赖注入模式。这个依赖反转原则在DDD中获得了很好的运用实践(参考前三篇)。
  
  (1) 演示:违反了依赖反转原则。缘由是:每当客户想要引入新的Logger记录形式时,咱们须要经过添加新方法来改变ExceptionLogger类。这里错误的体现了:高级类 ExceptionLogger直接引用低级类FileLogger和DbLogger来记录异常。
  
  复制代码
  
    /// <summary>
  
  /// 数据库日志类
  
  /// </summary>
  
  public class DbLogger
  
  {
  
  //写入日志
  
  public void LogMessage(string aMessage)
  
  {
  
  //Code to write message in database.
  
  }
  
  }
  
  /// <summary>
  
  /// 文件日志类
  
  /// </summary>
  
  public class FileLogger
  
  {
  
  //写入日志
  
  public void LogMessage(string aStackTrace)
  
  {
  
  //code to log stack trace into a file.
  
  }
  
  }
  
  public class ExceptionLogger
  
  {
  
  public void LogIntoFile(Exception aException)
  
  {
  
  FileLogger objFileLogger = new FileLogger();
  
  objFileLogger.LogMessage(GetUserReadableMessage(aException));
  
  }
  
  public void LogIntoDataBase(Exception aException)
  
  {
  
  DbLogger objDbLogger = new DbLogger();
  
  objDbLogger.LogMessage(GetUserReadableMessage(aException));
  
  }
  
  private string GetUserReadableMessage(Exception ex)
  
  {
  
  string strMessage = string.Empty;
  
  //code to convert Exception's stack trace and message to user readable format.
  
  return strMessage;
  
  }
  
  }
  
  public class DataExporter
  
  {
  
  public void ExportDataFromFile()
  
  {
  
  try
  
  {
  
  //code to export data from files to database.
  
  }
  
  catch (IOException ex)
  
  {
  
  new ExceptionLogger().LogIntoDataBase(ex);
  
  }
  
  catch (Exception ex)
  
  {
  
  new ExceptionLogger().LogIntoFile(ex);
  
  }
  
  }
  
  }
  
  复制代码
  
      (2) 解决方案,使用依赖反转原则,这里演示没有用依赖注入。
  
  复制代码
  
  public interface ILogger
  
  {
  
  void LogMessage(string aString);
  
  }
  
  /// <summary>
  
  /// 数据库日志类
  
  /// </summary>
  
  public class DbLogger : ILogger
  
  {
  
  //写入日志
  
  public void LogMessage(string aMessage)
  
  {
  
  //Code to write message in database.
  
  }
  
  }
  
  /// <summary>
  
  /// 文件日志类
  
  /// </summary>
  
  public class FileLogger : ILogger
  
  {
  
  //写入日志
  
  public void LogMessage(string aStackTrace)
  
  {
  
  //code to log stack trace into a file.
  
  }
  
  }
  
  public class ExceptionLogger
  
  {
  
  private ILogger _logger;
  
  public ExceptionLogger(ILogger aLogger)
  
  {
  
  this._logger = aLogger;
  
  }
  
  //能够与这些日志类达到松散耦合
  
  public void LogException(Exception aException)
  
  {
  
  string strMessage = GetUserReadableMessage(aException);
  
  this._logger.LogMessage(strMessage);
  
  }
  
  private string GetUserReadableMessage(Exception aException)
  
  {
  
  string strMessage = string.Empty;
  
  //code to convert Exception's stack trace and message to user readable format.
  
  return strMessage;
  
  }
  
  }
  
  public class DataExporter
  
  {
  
  public void ExportDataFromFile()
  
  {
  
  ExceptionLogger _exceptionLogger;
  
  try
  
  {
  
  //code to export data from files to database.
  
  }
  
  catch (IOException ex)
  
  {
  
  _exceptionLogger = new ExceptionLogger(new DbLogger());
  
  _exceptionLogger.LogException(ex);
  
  }
  
  catch (Exception ex)
  
  {
  
  _exceptionLogger = new ExceptionLogger(new FileLogger());
  
  _exceptionLogger.LogException(ex);
  
  }程序员

相关文章
相关标签/搜索