固态原理示例在哪里?

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

我正在与“ D ”挣扎。

我有时会将依赖性反转dependency injection混淆。 这是否意味着只要您依赖抽象(IE:接口)保持事物就完成了。

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

谢谢。

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

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

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

class CleanRiders { List GetCleanRiders() { var riderRepository = new MsSqlRiderRepository(); riderRepository.GetRiders.Where(x => x.Doping == false); } } 

该类依赖于MsSqlRiderRepository 。 该类控制实例的创建。 问题是这种依赖性是不灵活的。 很难将其更改为OracleRiderRepositoryTestRiderRepository

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

 class CleanRiders { private IRiderRepository _repository; public CleanRiders(IRiderRepository repository) { _repository = repository; } List GetCleanRiders() { _repository.GetRiders.Where(x => x.Doping == false); } } 

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

 void Main() { var c = new CleanRiders(new MsSqlRepository()); var riders = c.GetRiders(); } 

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

我试图在前几天向我的同事解释这一点,在这个过程中我甚至自己都理解了这个概念。 特别是当我想出现实生活中依赖倒置的现实例子时。

故事

想象一下,如果一个汽车司机依赖汽车:只能驾驶一辆汽车 – 汽车! 这将是非常糟糕的:

直接/硬依赖

在这种情况下,依赖关系的方向是:Driver => Car(Driver对象依赖于Car对象)。

值得庆幸的是,在现实生活中,每辆车都有界面: “方向盘,踏板和变速杆” 。 司机不再依赖于汽车,所以司机可以驾驶任何汽车:

依赖倒置

现在TheDriver依赖于ICar接口,TheCar也依赖于ICar接口 – 依赖是INVERTED:

我不像其他人那样专家,但会用概念解释DIP。 DIP的核心是接口的程序,即您的高级类将依赖于抽象,而您的低级类也依赖于抽象。 例如

让我们说你定义了一个名为PhoneVendor的抽象,即它可以是三星,苹果,诺基亚等。对于我已经有一段时间没有编写Java的代码抱歉了,即它可能有语法错误,但它仍然是关于这个概念。

 public abstract class PhoneVendor { /** * Abstract method that returns a list of phone types that each vendor creates. */ public abstract Vector getPhones(){ } } public class Samsung extends PhoneVendor{ public Vector getPhones(){ // return a list of phones it manufactures... } } public class PhoneFinder{ private PhoneVendor vendor; public PhoneFinder(PhoneVendor vendor){ this.vendor = vendor;} /** *for example just return a concatnated string of phones */ public string getPhoneTypes(){ Vector ListOfPhones = PhoneVendor.getPhones(); return ListOfPhones; } } 

正如您所看到的,PhoneFinder类取决于抽象而不是PhoneVendor的实现。 而实现抽象的基本类与使用它的高级类分离。 这使得设计非常灵活,添加新的低级别类不会破坏任何以前编写的代码,因为PhoneFinder依赖于抽象而不是实现。

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 } } 

要解决此问题,我们需要拆分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 } } 

设计界面和类时要考虑责任。 对课程的修改会涉及什么? 将课程分解成最简单的forms……但不是更简单(如爱因斯坦所说)。

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; } } 

但经销商不仅携带梅赛德斯! 这是课程不再可扩展的地方! 如果我们想加上其他汽车模型成本怎么办?!

 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; } } 

它现在坏了! 对于经销商批次中的每个车型,我们必须修改类并添加另一个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; } } 

这里问题解决了!

L:Liskov替代原则

SOL中的L指Liskov原理。 面向对象编程的inheritance概念可以固化,其中派生类不能以任何方式修改基类的行为。 我将回到LISKOV原理的现实世界的例子。 但是现在这就是原则本身:

T – >基地

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

I:接口分离原理

c#中的接口布局需要由实现接口的类实现的方法。 例如:

 Interface IAutomobile { public void SellCar(); public void BuyCar(); public void LeaseCar(); public void DriveCar(); public void StopCar(); } 

在此界面中,有两组活动正在进行中。 一组属于销售员,另一组属于驱动程序:

 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 */ } } 

在上面的类中,我们被迫实现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 */ } } 

我们现在也被迫实施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 */ } } 

接口隔离!

D:依赖性倒置原则

问题是:谁取决于谁?

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

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

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

所以我们开始创建我们的Controller(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; } } 

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

 public class Business { public void SaveEmployee(Employee empl) { Data myData = new Data(); myData.SaveEmployee(empl); } } 

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

现在让我们对Controller进行改进。 我们可以创建一个负责所有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(); } } 

现在让我们专注于PersistPeople课程。 它与Employee类硬编码并紧密耦合。 它在contstructor中接受一个Emloyee并实例化一个Business类来保存它。 如果我们想要保存“管理员”而不是“员工”怎么办? 现在我们的Persist类在Employee类上完全“依赖”。

让我们使用“Dependency Inversion”来解决这个问题。 但在此之前,我们需要创建一个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(); } } 

总而言之,我们的Persist类不依赖于硬编码到Employee类。 它可以使用任何数量的类型,如Employee,Admin等。保存现在传递的任何内容的控件都在于Persist类而不是HomeController。 Persist类现在知道如何保存传入的内容(Employee,Admin等)。 控制现在被反转并给予Persist类。 您还可以参考此博客,了解SOLID原则的一些很好的示例:

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

我希望这有帮助!