固态原理示例在哪里?

use*_*969 14 c# design-patterns

即使我们没有意识到,我们都会用一些模式编写代码.我试图真正理解一些SOLID原则以及如何在现实世界中应用这些原则.

我正在与" D " 挣扎.

我有时会将依赖性反转依赖注入混淆.这是否意味着只要您根据抽象(IE:接口)保留事物就完成了.

有没有人有一个小的C#示例解释它?

谢谢.

Dav*_*rne 18

看看Mark Seeman的博客,或者更好的是,购买他的书.它涵盖的不仅仅是DI.我很感激你可能只想要一个简单的样本.然而,这是一个主题,许多声称理解的人不会,因此值得学习.

也就是说,这是一个非常简单的例子.据我所知,术语是控制和依赖注入的反转.控制反转是指您将类的依赖关系控制到某个其他类的事实,与控制依赖关系本身的类相反,通常是通过new关键字.此控制通过依赖注入来执行,其中给出或注入了一个类及其依赖项.这可以通过IoC框架或代码(称为Pure DI)完成.可以在类的构造函数中,通过属性或作为方法的参数执行注入.依赖关系可以是任何类型,它们不必是抽象的.

这是一个列出没有掺杂的环法自行车赛冠军的课程:

class CleanRiders
{
    List<Rider> GetCleanRiders()
    {
        var riderRepository = new MsSqlRiderRepository();

        riderRepository.GetRiders.Where(x => x.Doping == false);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个课程依赖于MsSqlRiderRepository.该类控制实例的创建.问题是这种依赖性是不灵活的.将它改为a OracleRiderRepository或a 很难TestRiderRepository.

IoC和DI为我们解决了这个问题:

class CleanRiders
{
    private IRiderRepository _repository;

    public CleanRiders(IRiderRepository repository)
    {
        _repository = repository;
    }

    List<Rider> GetCleanRiders()
    {
        _repository.GetRiders.Where(x => x.Doping == false);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在该类只依赖于接口.对依赖项的控制已经被赋予类的创建者,并且必须通过其构造函数注入:

void Main()
{
    var c = new CleanRiders(new MsSqlRepository());

    var riders = c.GetRiders();
}
Run Code Online (Sandbox Code Playgroud)

可以说,一种更灵活,可测试和SOLID的方法.


小智 5

S:单一责任原则

以下代码有问题。“汽车”类包含两个不同的职责:首先是保养汽车模型,添加配件等,然后是第二责任:出售/租赁汽车。这会破坏SRP。这两个职责是分开的。

public Interface ICarModels {
}

public class Automobile : ICarModels {
   string Color { get; set; }
   string Model { get; set; }
   string Year { get; set; }
   public void AddAccessory(string accessory)
   {
      // Code to Add Accessory
   }

   public void SellCar()
   {
      // Add code to sell car
   }
   public void LeaseCar()
   {
      // Add code to lease car
   }
}
Run Code Online (Sandbox Code Playgroud)

要解决此问题,我们需要分解Automobile类并使用单独的接口:

public Interface ICarModels {
}

public class Automobile : ICarModels {
   string Color { get; set; }
   string Model { get; set; }
   string Year { get; set; }
   public void AddAccessory(string accessory)
   {
      // Code to Add Accessory
   }
}

public Interface ICarSales {
}

public class CarSales : ICarSales {
   public void SellCar()
   {
      // Add code to sell car
   }
   public void LeaseCar()
   {
      // Add code to lease car
   }
}
Run Code Online (Sandbox Code Playgroud)

在设计接口和类时,请考虑职责。对班级的修改涉及什么?将类分解为最简单的形式...但是没有任何简单的形式(就像爱因斯坦会说的那样)。

O:开放/封闭原则

当需求改变并且添加了更多类型用于处理时,类应该足够扩展,以使它们不需要修改。可以创建新类并将其用于处理。换句话说,类应该是可扩展的。我称之为“ If-Type”原则。如果您的代码中有很多if(type == ....),则需要将其分解为单独的类级别。

在此示例中,我们尝试计算经销商中汽车模型的总价。

public class Mercedes { 
   public double Cost { get; set; } 
} 

public class CostEstimation { 
   public double Cost(Mercedes[] cars) { 
     double cost = 0; 
     foreach (var car in cars) { 
        cost += car.Cost; } return cost; } 
}
Run Code Online (Sandbox Code Playgroud)

但是,经销商不仅运载梅赛德斯!这是该类不再可扩展的地方!如果我们也要加总其他车型的费用怎么办?

public class CostEstimation { 
   public double Cost(object[] cars)
   {
      double cost = 0;
      foreach (var car in cars)
      {
        if (car is Mercedes)
        {
            Mercedes mercedes = (Mercedes) car;
            cost += mercedes.cost;
        }
        else if (car is Volkswagen)
        {
            Volkswagen volks = (Volkswagen)car;
            cost += volks.cost;
        }
      }
      return cost;
   }
}
Run Code Online (Sandbox Code Playgroud)

现在坏了!对于经销店中的每种车型,我们必须修改类并添加另一个if语句!

因此,让我们对其进行修复:

public abstract class Car
{
    public abstract double Cost();
}

public class Mercedes : Car
{
    public double Cost { get; set; }
    public override double Cost()
    {
        return Cost * 1.2;
    }
}

public class BMW : Car
{
    public double Cost { get; set; }
    public override double Cost()
    {
        return Cost * 1.4;
    }
}

public class Volkswagen : Car
{
    public double Cost { get; set; }
    public override double Cost()
    {
        return Cost * 1.8;
    }
}

public class CostEstimation { 

   public double Cost(Car[] cars)
   {
    double cost = 0;
    foreach (var car in cars)
    {
        cost += car.Cost();
    }
    return cost;
   }
}
Run Code Online (Sandbox Code Playgroud)

问题到此解决!

L:利斯科夫换人原则

SOLID中的L表示Liskov原理。如果派生类无法以任何方式修改基类的行为,则可以巩固面向对象编程的继承概念。我将回到LISKOV原理的真实示例。但是现在这是原理本身:

T->基础

其中T [派生类]不应篡改Base的行为。

I:接口隔离原则

c#中的接口列出了需要由实现接口的类实现的方法。例如:

Interface IAutomobile { 
   public void SellCar();
   public void BuyCar();
   public void LeaseCar();
   public void DriveCar();
   public void StopCar();
}
Run Code Online (Sandbox Code Playgroud)

在此界面中,有两组活动正在进行。一个组属于推销员,另一组属于驾驶员:

public class Salesman : IAutomobile { 
   // Group 1: Sales activities that belong to a salesman
   public void SellCar() { /* Code to Sell car */ }
   public void BuyCar(); { /* Code to Buy car */ }
   public void LeaseCar(); { /* Code to lease car */ }

   // Group 2: Driving activities that belong to a driver
   public void DriveCar() { /* no action needed for a salesman */ }
   public void StopCar(); { /* no action needed for a salesman */ }
}
Run Code Online (Sandbox Code Playgroud)

在上面的类中,我们被迫实现DriveCar和StopCar方法。对于推销员没有意义的事情,不属于那里。

public class Driver : IAutomobile { 
   // Group 1: Sales activities that belong to a salesman
   public void SellCar() { /* no action needed for a driver */ }
   public void BuyCar(); { /* no action needed for a driver */ }
   public void LeaseCar(); { /* no action needed for a driver */ }

   // Group 2: Driving activities that belong to a driver
   public void DriveCar() { /* actions to drive car */ }
   public void StopCar(); { /* actions to stop car */ }
}
Run Code Online (Sandbox Code Playgroud)

我们现在不得不以同样的方式实施SellCar,BuyCar和LeaseCar。显然不属于Driver类的活动。

要解决此问题,我们需要将界面分为两部分:

Interface ISales { 
   public void SellCar();
   public void BuyCar();
   public void LeaseCar();
}

Interface IDrive {
   public void DriveCar();
   public void StopCar(); 
}

public class Salesman : ISales { 
   public void SellCar() { /* Code to Sell car */ }
   public void BuyCar(); { /* Code to Buy car */ }
   public void LeaseCar(); { /* Code to lease car */ }
}

public class Driver : IDrive { 
   public void DriveCar() { /* actions to drive car */ }
   public void StopCar(); { /* actions to stop car */ }
}
Run Code Online (Sandbox Code Playgroud)

接口隔离!

D:依赖倒置原则

问题是:谁依赖谁?

假设我们有一个传统的多层应用程序:

控制器层->业务层->数据层。

假设从Controller那里我们要告诉业务部门将Employee保存到数据库中。业务层要求数据层执行此操作。

因此,我们着手创建控制器(MVC示例):

public class HomeController : Controller { 
   public void SaveEmployee()
   {
       Employee empl = new Employee();
       empl.FirstName = "John";
       empl.LastName = "Doe";
       empl.EmployeeId = 247854;

       Business myBus = new Business();
       myBus.SaveEmployee(empl);
   }
}

public class Employee { 
 string FirstName { get; set; }
 string LastName { get; set; }
 int EmployeeId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,在我们的业务层中,我们有:

public class Business { 
   public void SaveEmployee(Employee empl)
   {
       Data myData = new Data();
       myData.SaveEmployee(empl);
   }
}
Run Code Online (Sandbox Code Playgroud)

在我们的数据层中,我们创建连接并将员工保存到数据库中。这是我们的传统三层体系结构。

现在让我们对控制器进行改进。我们可以创建一个类来处理所有Employee动作,而不是在我们的控制器内部使用SaveEmployee方法。

public class PersistPeople {
   Employee empl;
   // Constructor
   PersistPeople(Employee employee) {
      empl = employee;
   }
   public void SaveEmployee() {
     Business myBus = new Business();
     myBus.SaveEmployee();
   }    

   public Employee RetrieveEmployee() {
   } 

   public void RemoveEmployee() {
   }
}

// Now our HomeController is a bit more organized.
public class HomeController : Controller { 
   Employee empl = new Employee();
   empl.FirstName = "John";
   empl.LastName = "Doe";
   empl.EmployeeId = 247854;

   PersistPeople persist = new Persist(empl);
   persist.SaveEmployee();
   } 
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们专注于PersistPeople类。它与Employee类进行硬编码并紧密结合。它在构造函数中接收一个雇员,并实例化一个Business类来保存它。如果我们要保存“管理员”而不是“雇员”怎么办?现在,我们的Persist类完全“依赖” Employee类。

让我们使用“依赖倒置”来解决此问题。但是在此之前,我们需要创建一个Employee和Admin类都派生自的接口:

Interface IPerson { 
 string FirstName { get; set; }
 string LastName { get; set; }
 int EmployeeId { get; set; }
}

public class Employee : IPerson {
  int EmployeeId;
}

public class Admin : IPerson {
  int AdminId;
}

public class PersistPeople {
   IPerson person;
   // Constructor
   PersistPeople(IPerson person) {
      this.person = person;
   }
   public void SavePerson() {
      person.Save();
   }    
}

// Now our HomeController is using dependency inversion:
public class HomeController : Controller { 

   // If we want to save an employee we can use Persist class:
   Employee empl = new Employee();
   empl.FirstName = "John";
   empl.LastName = "Doe";
   empl.EmployeeId = 247854;
   PersistPeople persist = new Persist(empl);
   persist.SavePerson();

   // Or if we want to save an admin we can use Persist class:
   Admin admin = new Admin();
   admin.FirstName = "David";
   admin.LastName = "Borax";
   admin.EmployeeId = 999888;
   PersistPeople persist = new Persist(admin);
   persist.SavePerson();
   } 
}
Run Code Online (Sandbox Code Playgroud)

因此,总而言之,我们的Persist类不是依赖类,也不是硬编码到Employee类。它可以采用多种类型,例如Employee,Admin等。保存传入的内容的控件现在位于Persist类而非HomeController中。现在,Persist类知道如何保存传入的内容(Employee,Admin等)。现在将控制权转交给了Persist类。您也可以参考此博客,以获得SOLID原理的一些出色示例:

参考:https : //darkwareblog.wordpress.com/2017/10/17/

我希望这有帮助!