本文介绍了依赖性反转原则(SOLID)与封装(OOP的支柱)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近在讨论有关依赖性反转原则,反向控制依赖注入。关于这个话题,我们辩论这些原则是否违反了OOP的支柱之一,即封装。



我对这些事情的理解是:




  • 依赖性反转原则意味着对象应该取决于抽象,而不是结构 - 这是基本原则

  • 控制反转是依赖性反转原则的模式实现,其中抽象依赖关系取代具体的依赖关系允许在对象之外指定依赖关系的结构。

  • 依赖注入是一种实现Inversion of Control并提供依赖关系解析的设计模式。当依赖关系传递给依赖组件时,会发生注入。实质上,依赖注入模式提供了一种将依赖关系抽象与具体实现相结合的机制。

  • 封装是一个更高级别所需的数据和功能的过程程序员不知道如何实现对象。



辩论得到了坚持指出以下声明:

个人而言,我认为所有OOP开发人员都应该遵守依赖性反转原则和反转控制模式 - 我以下列引用:

示例1:

  class Program {
void Main(){
SkinCatWithKnife skinner = new SkinCatWithKnife();
skinner.SkinTheCat();
}
}

这里我们看到一个封装的例子。程序员只需要调用 Main(),那么猫会被皮肤剥去,但是如果他想用一只剃刀锋利的牙齿去皮肤呢? p>

示例2:

  
//封装
ICatSkinner skinner;

public程序(ICatSkinner skinner){
//控件的反转
this.skinner = skinner;
}

void Main(){
this.skinner.SkinTheCat();
}
}

...新程序(新的SkinCatWithTeeth());
//依赖注入

这里我们观察到依赖性反转原则和反转控制,因为提供抽象( ICatSkinner ),以便程序员可以传递具体的依赖关系。最后,有不止一种方式来抚养一只猫!



这里的争吵是这是否打破封装?技术上可以认为 .SkinTheCat(); 仍然封装在 Main()方法调用中,因此程序员不知道这种方法的行为,所以我不认为这会打破封装。



深入了解一下,我认为IoC 容器因为使用反射来打破OOP,但我不相信IoC会打破OOP,也不相信IoC会打破封装。实际上,我会去说:

问题:




  • IoC是依赖性反转原则的直接实现吗?

  • IoC是否总是打破封装,因此OOP?

  • 应该谨慎,宗教还是适当地使用IoC?

  • IoC与IoC容器之间的区别?


解决方案

不,这些是分层相关的问题。封装是OOP中最被误解的概念之一,但我认为这种关系最好通过抽象数据类型(ADT)来描述。基本上,ADT是数据和相关行为的一般描述。这个描述是抽象的它省略了实现细节。相反,它根据 pre - 后置条件描述ADT。



这是Bertrand Meyer根据合同呼叫设计。您可以在中阅读有关OOD的这一重要描述。



对象通常被描述为具有行为的数据。这意味着没有数据的对象不是真正的对象。因此,您必须以某种方式获取数据。



例如,您可以通过其构造函数将数据传递到对象中:

  public class Foo 
{
private readonly int bar;

public Foo(int bar)
{
this.bar = bar;
}

//其他成员可以以各种方式使用this.bar。
}

另一个选项是使用setter函数或属性。我希望我们可以同意,迄今为止,封装不被侵犯。



如果我们从一个更改 bar 整数到另一个具体类?

  public class Foo 
{
private readonly Bar bar;

public Foo(酒吧吧)
{
this.bar = bar;
}

//其他成员可以以各种方式使用this.bar。
}

与之前的唯一区别是 bar 现在是一个对象,而不是一个原始的。然而,这是一个错误的区别,因为在面向对象的设计中,整数也是一个对象。这只是因为各种编程语言(Java,C#等)中的性能优化,原语(字符串,整数,布尔等)和真实对象之间存在实际差异。从OOD的角度来看,他们都是一样的。字符串也有行为:你可以把它们变成全大写,反向等等。



如果 Bar 是一个密封/最终的具体类,只有非虚拟成员?



bar 只有具有行为的数据,就像一个整数,但除此之外,没有什么区别。到目前为止,封装不被侵犯。



如果允许 Bar 拥有一个虚拟成员,会发生什么?



封装是否被破坏?



我们还可以表达关于 Foo ,假设 Bar 有一个虚拟成员?



如果 Bar 遵守(LSP),它将不会有什么变化LSP明确规定,改变行为不能改变系统的正确性。只要合同被实现,封装仍然完整。



因此,LSP(,其中是另一个)没有违反封装;它描述了在多态性存在

$ c> Bar 是一个抽象基类?一个界面?



不,它不是:那些只是不同程度的多态。因此,我们可以将 Bar 重命名为 IBar (以便建议它是一个界面),并将其传递到 Foo 作为其数据:

  public class Foo 
{
私人只读IBar酒吧;

public Foo(IBar bar)
{
this.bar = bar;
}

//其他成员可以以各种方式使用this.bar。
}

bar 只是另一个多态对象,只要LSP保持,封装保持。



TL; DR



有一个原因SOLID也被称为的原则。封装(即按合约设计)定义了基本规则。 SOLID描述遵循这些规则的指导原则。


I was recently having a debate about the Dependency Inversion Principle, Inversion of Control and Dependency Injection. In relation to this topic we were debating whether these principles violate one of the pillars of OOP, namely Encapsulation.

My understanding of these things is:

  • The Dependency Inversion Principle implies that objects should depend upon abstractions, not concretions - this is the fundamental principle upon which the Inversion of Control pattern and Dependency Injection are implemented.
  • Inversion of Control is a pattern implementation of the Dependency Inversion Principle, where abstract dependencies replace concrete dependencies, allowing concretions of the dependency to be specified outside of the object.
  • Dependency Injection is a design pattern that implements Inversion of Control and provides dependency resolution. Injection occurs when a dependency is passed to a dependent component. In essence, the Dependency Injection pattern provides a mechanism for coupling dependency abstractions with concrete implementations.
  • Encapsulation is the process whereby data and functionality that is required by a higher level object is insulated away and inaccessible, thus, the programmer is unaware of how an object is implemented.

The debate got to a sticking point with the following statement:

Personally, I think that the Dependency Inversion Principle and the Inversion of Control pattern should be observed religiously by all OOP developers - and I live by the following quote:

Example 1:

class Program {
    void Main() {
        SkinCatWithKnife skinner = new SkinCatWithKnife ();
        skinner.SkinTheCat();
    }
}

Here we see an example of encapsulation. The programmer only has to call Main() and the cat will be skinned, but what if he wanted to skin the cat with, say a set of razor sharp teeth?

Example 2:

class Program {
    // Encapsulation
    ICatSkinner skinner;

    public Program(ICatSkinner skinner) {
        // Inversion of control
        this.skinner = skinner;
    }

    void Main() {
        this.skinner.SkinTheCat();
    }
}

... new Program(new SkinCatWithTeeth());
    // Dependency Injection

Here we observe the Dependency Inversion Principle and Inversion of Control since an abstract (ICatSkinner) is provided in order to allow concrete dependencies to be passed in by the programmer. At last, there is more than one way to skin a cat!

The quarrel here is; does this break encapsulation? technically one could argue that .SkinTheCat(); is still encapsulated away within the Main() method call, so the programmer is unaware of the behavior of this method, so I do not think this breaks encapsulation.

Delving a little deeper, I think that IoC containers break OOP because they use reflection, but I am not convinced that IoC breaks OOP, nor am I convinced that IoC breaks encapsulation. In fact I'd go as far as to say that:

Questions:

  • Is IoC a direct implementation of the Dependency Inversion Principle?
  • Does IoC always break encapsulation, and therefore OOP?
  • Should IoC be used sparingly, religiously or appropriately?
  • What is the difference between IoC and an IoC container?

解决方案

No, these are hierarchically related concerns. Encapsulation is one of the most misunderstood concepts in OOP, but I think the relationship is best described via Abstract Data Types (ADTs). Essentially, an ADT is a general description of data and associated behaviour. This description is abstract; it omits implementation details. Instead, it describes an ADT in terms of pre- and post-conditions.

This is what Bertrand Meyer calls design by contract. You can read more about this seminal description of OOD in Object-Oriented Software Construction.

Objects are often described as data with behaviour. This means that an object without data isn't really an object. Thus, you have to get data into the object in some way.

You could, for example, pass data into an object via its constructor:

public class Foo
{
    private readonly int bar;

    public Foo(int bar)
    {
        this.bar = bar;
    }

    // Other members may use this.bar in various ways.
}

Another option is to use a setter function or property. I hope we can agree that so far, encapsulation is not violated.

What happens if we change bar from an integer to another concrete class?

public class Foo
{
    private readonly Bar bar;

    public Foo(Bar bar)
    {
        this.bar = bar;
    }

    // Other members may use this.bar in various ways.
}

The only difference compared to before is that bar is now an object, instead of a primitive. However, that's a false distinction, because in object-oriented design, an integer is also an object. It's only because of performance optimisations in various programming languages (Java, C#, etc.) that there's an actual difference between primitives (strings, integers, bools, etc.) and 'real' objects. From an OOD perspective, they're all alike. Strings have behaviours as well: you can turn them into all-upper-case, reverse them, etc.

Is encapsulation violated if Bar is a sealed/final, concrete class with only non-virtual members?

bar is only data with behaviour, just like an integer, but apart from that, there's no difference. So far, encapsulation isn't violated.

What happens if we allow Bar to have a single virtual member?

Is encapsulation broken by that?

Can we still express pre- and post-conditions about Foo, given that Bar has a single virtual member?

If Bar adheres to the Liskov Substitution Principle (LSP), it wouldn't make a difference. The LSP explicitly states that changing the behaviour mustn't change the correctness of the system. As long as that contract is fulfilled, encapsulation is still intact.

Thus, the LSP (one of the SOLID principles, of which the Dependency Inversion Principle is another) doesn't violate encapsulation; it describes a principle for maintaining encapsulation in the presence of polymorphism.

Does the conclusion change if Bar is an abstract base class? An interface?

No, it doesn't: those are just different degrees of polymorphism. Thus we could rename Bar to IBar (in order to suggest that it's an interface) and pass it into Foo as its data:

public class Foo
{
    private readonly IBar bar;

    public Foo(IBar bar)
    {
        this.bar = bar;
    }

    // Other members may use this.bar in various ways.
}

bar is just another polymorphic object, and as long as the LSP holds, encapsulation holds.

TL; DR

There's a reason SOLID is also known as the Principles of OOD. Encapsulation (i.e. design-by-contract) defines the ground rules. SOLID describes guidelines for following those rules.

这篇关于依赖性反转原则(SOLID)与封装(OOP的支柱)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-25 20:22