Oop 依赖倒置原则是什么?为什么它很重要?
依赖项反转原则是什么?为什么它很重要?查看此文档: 它基本上说:Oop 依赖倒置原则是什么?为什么它很重要?,oop,solid-principles,glossary,principles,dependency-inversion,Oop,Solid Principles,Glossary,Principles,Dependency Inversion,依赖项反转原则是什么?为什么它很重要?查看此文档: 它基本上说: 高级模块不应依赖于低级模块。两者都应该依赖于抽象 抽象永远不应该依赖于细节。细节应该取决于抽象 简而言之,关于为什么它很重要:更改是有风险的,通过依赖一个概念而不是一个实现,您可以减少呼叫站点更改的需要 DIP有效地减少了不同代码段之间的耦合。这个想法是,尽管有很多方法可以实现,比如说,日志记录工具,但是您使用它的方式在时间上应该是相对稳定的。如果您可以提取一个表示日志记录概念的接口,那么这个接口在时间上应该比它的实现更稳定,
- 高级模块不应依赖于低级模块。两者都应该依赖于抽象
- 抽象永远不应该依赖于细节。细节应该取决于抽象
通过使实现依赖于接口,您可以在运行时选择哪个实现更适合您的特定环境。根据不同的情况,这可能也很有趣。依赖倒置的要点是使软件可重用 其思想是,它们依赖于某种抽象接口,而不是两段相互依赖的代码。然后,您可以重复使用其中一个,而不使用另一个 最常见的实现方式是通过控制反转(InversionofControl,IoC)容器,如Java中的Spring。在这个模型中,对象的属性是通过XML配置设置的,而不是对象出去寻找它们的依赖关系 想象一下这个伪代码
public class MyClass
{
public Service myService = ServiceLocator.service;
}
MyClass直接依赖于服务类和ServiceLocator类。如果您想在另一个应用程序中使用它,它需要这两个方面。现在想象一下
public class MyClass
{
public IService myService;
}
现在,MyClass依赖于一个接口,即IService接口。我们让IoC容器实际设置该变量的值
所以现在,MyClass可以很容易地在其他项目中重用,而不会带来其他两个类的依赖关系
更好的是,您不必拖拽MyService的依赖项,以及这些依赖项的依赖项,以及。。。好吧,你明白了。(IoC)是一种设计模式,在这种模式中,对象由外部框架传递其依赖关系,而不是要求框架提供其依赖关系
使用传统查找的伪代码示例:
class Service {
Database database;
init() {
database = FrameworkSingleton.getService("database");
}
}
使用IoC的类似代码:
class Service {
Database database;
init(database) {
this.database = database;
}
}
国际奥委会的好处是:
- 您不依赖于中央处理器 框架,因此如果 想要的
- 因为对象是创建的 通过注射,最好使用 接口,很容易创建单元 将依赖项替换为 模拟版本
- 分离代码
- 这里的其他人已经给出了好的答案和好的例子
原因很重要,因为它确保了OO原则“松耦合设计”
软件中的对象不应进入层次结构,其中某些对象是顶级对象,依赖于低级对象。低级对象中的更改随后会波及到顶级对象,这使得软件对于更改非常脆弱
您希望您的“顶级”对象非常稳定且不易更改,因此您需要反转依赖关系。敏捷软件开发、原则、模式和实践以及敏捷原则、模式、,C#中的实践是充分理解依赖倒置原则背后的原始目标和动机的最佳资源。“依赖倒置原则”一文也是一个很好的资源,但由于它是一个草案的浓缩版本,最终进入了前面提到的书中,它省略了一些关于包和接口所有权概念的重要讨论,这是区分这一原则与书籍设计模式(Gamma等)中更一般的建议“编程到接口,而不是实现”的关键 为了提供一个总结,依赖项反转原则主要是关于将依赖项的传统方向从“更高级别”组件反转为“更低级别”组件,从而“更低级别”组件依赖于“更高级别”组件所拥有的接口。(注意:“更高级别”组件在此指需要外部依赖项/服务的组件,而不一定指其在分层体系结构中的概念位置。),耦合并没有减少,而是从理论上价值较低的组件转移到了理论上价值较高的组件 这是通过设计组件来实现的,这些组件的外部依赖关系以接口的形式表示,而接口的实现必须由组件的使用者提供。换句话说,定义的接口表示组件需要什么,而不是如何使用组件(例如,“INeedSomething”,而不是“idosmething”) 依赖倒置原则没有提到的是通过使用接口(例如MyService)抽象依赖关系的简单实践→ [伊洛格⇐ 记录器])。虽然这将组件与依赖项的特定实现细节分离,但它不会颠倒使用者和依赖项之间的关系(例如,[MyService])→ IMyServiceLogger]⇐ 记录器 依赖倒置原则的重要性可以归结为一个单一的目标,即能够重用软件组件,这些组件依赖于一部分软件的外部依赖性
// DataAccessLayer.dll
public class ProductDAO {
}
// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
private ProductDAO productDAO;
}
// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{
}
// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
private IProductDAO productDAO;
}
// Domain.dll
public interface IProductRepository;
using DataAccessLayer;
public class ProductBO {
private IProductRepository productRepository;
}
// Persistence.dll
public class ProductDAO : IProductRepository{
}
class Dependency { ... }
class Logic {
private Dependency dep;
int doSomething() {
// Business logic using dep here
}
}
class Dependency { ... }
interface Data { ... }
class DataFromDependency implements Data {
private Dependency dep;
...
}
class Logic {
int doSomething(Data data) {
// compute something with data
}
}
public interface ICustomer
{
string GetCustomerNameById(int id);
}
public class Customer : ICustomer
{
//ctor
public Customer(){}
public string GetCustomerNameById(int id)
{
return "Dummy Customer Name";
}
}
public class CustomerFactory
{
public static ICustomer GetCustomerData()
{
return new Customer();
}
}
public class CustomerBLL
{
ICustomer _customer;
public CustomerBLL()
{
_customer = CustomerFactory.GetCustomerData();
}
public string GetCustomerNameById(int id)
{
return _customer.GetCustomerNameById(id);
}
}
public class Program
{
static void Main()
{
CustomerBLL customerBLL = new CustomerBLL();
int customerId = 25;
string customerName = customerBLL.GetCustomerNameById(customerId);
Console.WriteLine(customerName);
Console.ReadKey();
}
}
class Program
{
static void Main(string[] args)
{
/*
* BadEncoder: High-level class *contains* low-level I/O functionality.
* Hence, you'll have to fiddle with BadEncoder whenever you want to change
* the I/O mode or details. Not good. A good encoder should be I/O-agnostic --
* problems with I/O shouldn't break the encoder!
*/
BadEncoder.Run();
}
}
public static class BadEncoder
{
public static void Run()
{
Console.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(Console.ReadLine())));
}
}
class Program
{
static void Main(string[] args)
{
/* Demo of the Dependency Inversion Principle (= "High-level functionality
* should not depend upon low-level implementations"):
* You can easily implement new I/O methods like
* ConsoleReader, ConsoleWriter without ever touching the high-level
* Encoder class!!!
*/
GoodEncoder.Run(new ConsoleReader(), new ConsoleWriter()); }
}
public static class GoodEncoder
{
public static void Run(IReadable input, IWriteable output)
{
output.WriteOutput(Convert.ToBase64String(Encoding.ASCII.GetBytes(input.ReadInput())));
}
}
public interface IReadable
{
string ReadInput();
}
public interface IWriteable
{
void WriteOutput(string txt);
}
public class ConsoleReader : IReadable
{
public string ReadInput()
{
return Console.ReadLine();
}
}
public class ConsoleWriter : IWriteable
{
public void WriteOutput(string txt)
{
Console.WriteLine(txt);
}
}
class Student {
private Address address;
public Student() {
this.address = new Address();
}
}
class Address{
private String perminentAddress;
private String currentAdrress;
public Address() {
}
}
class Student{
private Address address;
public Student(Address address) {
this.address = address;
}
//or
public void setAddress(Address address) {
this.address = address;
}
}
//A -> B
class A {
B b
func foo() {
b = B();
}
}
//A -> IB <|- B
//client[A -> IB] <|- B is the Inversion
class A {
B ib
func foo() {
ib = B()
}
}