Principe d'Inversion de dépendance (solide) vs Encapsulation (piliers de la POO)

j'ai récemment eu un débat sur le Principe D'Inversion De Dépendance, Inversion du contrôle et Injection De Dépendance. En ce qui concerne ce sujet, nous débattions si ces principes violent l'un des piliers de L'OOP, à savoir Encapsulation.

Ma compréhension de ces choses, c'est:

  • Principe D'Inversion De Dépendance implique que les objets devraient dépendre abstractions, pas concrétions - c'est le principe fondamental sur lequel L'Inversion du modèle de contrôle et L'Injection de dépendance sont mises en œuvre.
  • Inversion du contrôle est une implémentation de pattern du principe D'Inversion de dépendances, où les dépendances abstraites remplacent les dépendances concrètes, permettant de spécifier des concrétions de la dépendance en dehors de l'objet.
  • Injection De Dépendance est un motif qui implémente L'Inversion du contrôle et fournit la résolution des dépendances. L'Injection se produit lorsqu'une dépendance est passée à une composante dépendante. Essentiellement, le modèle D'Injection de dépendances fournit un mécanisme pour coupler des abstractions de dépendances avec des implémentations concrètes.
  • Encapsulation est le processus par lequel les données et les fonctionnalités requises par un objet de niveau supérieur sont isolées et inaccessibles, donc, le programmeur ne sait pas comment un objet est mettre.

Le débat est arrivé à un point de désaccord avec l'énoncé suivant:

Cio n'est pas de la POO, car il se décompose Encapsulation

personnellement, je pense que le principe de L'Inversion de dépendance et L'Inversion du modèle de contrôle devraient être observés religieusement par tous les développeurs OOP - et je vis par la citation suivante:

S'il y a (potentiellement) plus d'une façon d'écorcher un chat, alors ne pas se comporter comme s'il n'y en a qu'une.

exemple 1:

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

voici un exemple d'encapsulation. Le programmeur n'a qu'à appeler Main() et le chat sera écorché, mais que faire s'il voulait écorcher le chat avec, disons un ensemble de dents aiguisées au rasoir?

exemple 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

nous observons ici le principe D'Inversion de dépendance et L'Inversion de contrôle depuis un Abstrait (ICatSkinner) est fourni afin de permettre au programmeur de passer des dépendances concrètes. enfin, il y a plus d'une façon d'écorcher un chat!

La querelle est ici; est-ce à briser l'encapsulation? techniquement, on pourrait dire que .SkinTheCat(); est toujours encapsulé à l'intérieur du Main() appel de méthode, donc le programmeur n'est pas au courant du comportement de cette méthode, donc je ne pense pas que cela brise l'encapsulation.

en approfondissant un peu, je pense que Cio récipients saut de la POO, car ils utilisent la réflexion, mais je ne suis pas convaincu que le Cio pauses de la POO, et je ne suis pas convaincu que le Cio pauses de l'encapsulation. En fait, j'irais jusqu'à dire que:

L'Encapsulation et L'Inversion des commandes coïncident les unes avec les autres heureusement, permettant aux programmeurs de passer seulement les concrétions d'une la dépendance, tout en dissimulant la mise en œuvre globale via encapsulation.

Questions:

  • Cio Est une mise en œuvre directe de l'Inversion de Dépendance Principe?
  • le CIO casse-t-il toujours l'encapsulation, et donc L'OOP?
  • le CIO devrait-il être utilisé avec parcimonie, religieusement ou de manière appropriée?
  • Quelle est la différence entre un conteneur CIO et un conteneur CIO?
38
demandé sur series0ne 2015-06-29 20:10:46

7 réponses

le CIO casse-t-il toujours l'encapsulation, et donc L'OOP?

Non, il s'agit de préoccupations liées à la hiérarchie. L'Encapsulation est l'un des concepts les plus mal compris dans OOP, mais je pense que la relation est mieux décrite via les types de données abstraites (ADTs). Essentiellement, un ADT est une description générale des données et des comportements associés. Cette description est abstraite; elle omet les détails de la mise en œuvre. Au lieu de cela, il décrit un ADT en termes de - et post-conditions.

C'est ce que Bertrand Meyer appelle conception par contrat. Vous pouvez en savoir plus sur cette description originale de L'OD en Construction De Logiciels Orientés Objet.

les objets sont souvent décrits comme données de comportement. Cela signifie qu'un objet sans les données ne sont pas vraiment un objet. Ainsi, vous devez obtenir les données dans l'objet dans certains façon.

vous pouvez, par exemple, passer des données dans un objet via son constructeur:

public class Foo
{
    private readonly int bar;

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

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

une autre option est d'utiliser une fonction ou une propriété de setter. J'espère que nous pouvons convenir que jusqu'à présent, l'encapsulation n'est pas violé.

Qu'advient-il si nous changeons bar d'un entier à une autre classe concrète?

public class Foo
{
    private readonly Bar bar;

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

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

la seule différence par rapport à avant est que bar est maintenant un objet, au lieu d'une primitive. Cependant, c'est une fausse distinction, parce que dans la conception orientée objet, un entier est aussi un objet. C'est uniquement à cause de l'optimisation des performances dans différents langages de programmation (Java, C#, etc.) qu'il y a une différence réelle entre les primitives (chaînes, entiers, bools, etc.) et "réel" des objets. D'un point de vue OOD, ils sont tous pareils. Les chaînes ont des comportements: vous pouvez les transformer en tout cas supérieur, les inverser, etc.

Est l'encapsulation est violé si Bar est un classe de béton scellée / finale avec seulement des membres non virtuels?

bar est seulement des données avec un comportement, tout comme un entier, mais à part cela, il n'y a aucune différence. Jusqu'à présent, l'encapsulation n'est pas violé.

Ce qui se passe si nous permettons Bar pour avoir un seul membre virtuel?

l'encapsulation est-elle brisée par cela?

peut - on encore exprimer des pré-et post-conditions à propos de Foo, étant donné que Bar a virtuel unique membre?

Si Bar adhère à l' Principe De Substitution De Liskov (LSP), il ne fera pas une différence. La LSP stipule explicitement que la modification du comportement ne doit pas modifier l'exactitude du système. Tant que l' contrat est remplie, l'encapsulation est toujours intacte.

ainsi, le LSP (l'un des principes SOLIDES, qui Principe D'Inversion De Dépendance est un autre) ne viole pas les l'encapsulation; il décrit principe entretien encapsulation en présence de polymorphisme.

la conclusion le changement si Bar est une classe de base abstraite? Une interface?

Non, ça ne l'est pas: ce sont juste des degrés différents de polymorphisme. Ainsi, nous pourrions renommer BarIBar (afin de suggérer que c'est une interface) et le passer dans Foo comme ses données:

public class Foo
{
    private readonly IBar bar;

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

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

bar est juste un autre objet polymorphe, et aussi longtemps que le LSP tient, encapsulation tient.

TL; DR

Il y a une raison SOLIDE est également connu comme les Principes de CRUES. L'Encapsulation (c'est à dire la conception par contrat) définit les règles de base. SOLID décrit les lignes directrices pour suivre ces règles.

40
répondu Mark Seemann 2015-06-30 11:05:29

la COI est-elle une application directe du principe de L'Inversion de dépendance?

Les deux sont liés d'une manière qu'ils parlent des abstractions, mais c'est tout. L'Inversion du contrôle est:

une conception dans laquelle la mesure des portions d'un programme d'ordinateur recevoir le flux de contrôle d'un générique, bibliothèque réutilisable ( source)

L'Inversion du contrôle nous permet d'accrocher notre code personnalisé dans le pipeline d'une bibliothèque réutilisable. En d'autres termes, le contrôle des inversions concerne les cadres. Une bibliothèque réutilisable qui n'applique pas L'Inversion de contrôle n'est qu'une bibliothèque. Un cadre est une bibliothèque réutilisable que appliquer une Inversion de commande.

notez que nous en tant que développeurs ne pouvons appliquer L'Inversion de contrôle que si nous écrivons nous-mêmes un framework; vous ne pouvez pas appliquer l'inversion de contrôle en tant que développeur d'applications. Nous pouvons (et devrions) cependant appliquer la dépendance Principe d'Inversion et schéma D'Injection de dépendance.

le CIO casse-t-il toujours l'encapsulation, et donc L'OOP?

comme IoC est sur le point de s'accrocher dans le pipeline d'un cadre, il n'y a rien qui fuit ici. Donc la vraie question Est: est-ce que L'Injection de dépendance casse l'encapsulation.

La réponse à cette question est: non, il n'est pas. Il ne casse pas l'encapsulation pour deux raisons:

  • Depuis les principes D'Inversion de dépendance stipulent que nous devrions programmer contre une abstraction, un consommateur ne pourra pas accéder aux internes de l'implémentation utilisée et que l'implémentation ne brisera donc pas l'encapsulation au client. L'implémentation peut même ne pas être connue ou accessible au moment de la compilation (parce qu'elle vit dans un assemblage non référencé) et l'implémentation peut dans ce cas ne pas laisser échapper les détails de l'implémentation et casser l'encapsulation.
  • Bien que l'implémentation accepte les dépendances dont elle a besoin tout au long de son constructeur, ces dépendances seront typiquement stockées dans des champs privés et ne seront accessibles à personne (même si un consommateur dépend directement du type concret) et ne casseront donc pas l'encapsulation.

le CIO doit-il être utilisé avec parcimonie, religieusement ou de manière appropriée?

encore une fois, la question Est "devrait tremper et DI être utilisé avec parcimonie". À mon avis, la réponse est: Non, vous devriez l'utiliser tout au long de l'application. Évidemment, vous ne devriez jamais appliquer les choses religieusement. Vous devez appliquer des principes solides et le DIP est un élément crucial de ces principes. Ils rendront votre application plus flexible et plus facile à entretenir et dans la plupart des scénarios, il est très approprié d'appliquer les principes solides.

Quelle est la différence entre un conteneur CIO et un conteneur CIO?

Injection de Dépendance est un modèle qui peut être appliqué avec ou sans conteneur CIO. Un conteneur IoC est simplement un outil qui peut aider à construire votre objet graphique d'une manière plus commode, dans le cas où vous avez une application qui applique les principes solides correctement. Si vous avez une application qui n'applique pas les principes solides, vous aurez du mal à utiliser un conteneur CIO. Vous aurez du mal à appliquer L'Injection de dépendance. Ou laissez-moi le dire plus largement, vous aurez du mal à maintenir votre application de toute façon. Mais aucun un conteneur IoC est un outil nécessaire. Je développe et maintiens un conteneur IoC pour .NET, mais je n'utilise pas toujours un conteneur pour mes applications. Pour les gros BLOBAs (boring line of business applications) j'utilise souvent un conteneur, mais pour les applications plus petites (ou les Services windows) je n'utilise pas toujours un conteneur. Mais J' presque toujours utiliser L'Injection de dépendances comme modèle, parce que c'est la façon la plus efficace de s'y conformer PLONGER.

Note: Puisqu'un conteneur IoC nous aide à appliquer le schéma D'Injection de dépendance, "conteneur IoC" est un nom terrible pour une telle bibliothèque.

mais en dépit de tout ce que j'ai dit ci-dessus, s'il vous plaît noter que:

dans le monde réel par le développeur du logiciel, l'utilité l'emporte sur la théorie [de Robert C. Martin Principe, modèles et pratiques agiles]

en d'autres termes, même si DI se briserait encapsulation, cela n'aurait pas d'importance, parce que ces techniques et modèles se sont avérés très précieux, parce qu'il en résulte des systèmes très flexibles et maintenables. La pratique l'emporte sur la théorie.

32
répondu Steven 2015-10-27 20:01:33

pour résumer la question:

nous avons la possibilité pour un Service d'instancier ses propres dépendances.

pourtant, nous avons aussi la possibilité pour un Service de simplement définir des abstractions, et d'exiger une application pour connaître les abstractions dépendantes, créer des implémentations concrètes, et les transmettre.

Et la question n'est pas, " pourquoi le faisons-nous?" (Parce que nous savons qu'il ya une énorme liste de pourquoi). Mais la question est de, "N'a pas l'option 2 de briser l'encapsulation?"

ma réponse "pragmatique"

L'Encapsulation cache les détails d'implémentation d'un service ou d'une abstraction. Une Dépendance n'est pas un détail d'implémentation. Si vous considérez un service comme un contrat, et ses dépendances ultérieures comme des contrats de sous-traitance (etc. etc enchaîné le long), alors vous venez vraiment de finir avec un contrat énorme avec addendums.

Imaginez que je suis un appelant et je veux utiliser un service juridique pour poursuivre mon patron. Ma demande à propos d'un service qui le fait. Cela suffit à briser la théorie selon laquelle connaître les services/contrats requis pour atteindre mon objectif est faux.

L'argument est... Ouais, mais je veux juste engager un avocat, Je ne me soucie pas de quels livres ou services il utiliser. Je vais prendre un peu de sang au hasard sur l'interwebz et ne se soucie pas de ses détails de mise en œuvre... comme ceci:

sub main() {
    LegalService legalService = new LegalService();

    legalService.SueMyManagerForBeingMean();
}

public class LegalService {
    public void SueMyManagerForBeingMean(){
        // Implementation Details.
    }
}
sub main() {
    IWorkLawService understandWorkplaceLaw = new CaliforniaWorkplaceLawService();
    //IWorkLawService understandWorkplaceLaw = new NewYorkWorkplaceLawService();
    LegalService legalService = new LegalService(understandWorkplaceLaw);

    legalService.SueMyManagerForBeingMean();
}

public interface ILegalContract {
    void SueMyManagerForBeingMean();
}

public class LegalService : ILegalContract {
    private readonly IWorkLawService _workLawService;

    public LegalService(IWorkLawService workLawService) {
        this._workLawService = workLawService;
    }

    public void SueMyManagerForBeingMean() {
        //Implementation Detail
        _workLawService.DoSomething; // { implementation detail in there too }
    }
}

Maintenant, tout ce que je sais, c'est que j'ai un contrat qui a d'autres contrats qui pourraient avoir d'autres contrats. Je suis très responsable pour ces contrats, et non de leur mise en œuvre. Bien que je sois plus qu'heureux de signer ces contrats avec des concrétions qui sont en rapport avec mes exigences. Et encore une fois, je ne me soucie pas de la façon dont ces concrétions font leur emplois, aussi longtemps que je sais que j'ai un contrat qui dit que nous échanger des informations d'une certaine manière définie.

1
répondu Suamere 2015-07-14 17:10:04

je vais essayer de répondre à vos questions, Selon ma compréhension:

  • la COI est-elle une application directe du principe de L'Inversion de dépendance?

    nous ne pouvons pas étiqueter coi comme étant l'implémentation directe de DIP , car DIP se concentre sur la fabrication de modules de niveau supérieur en fonction de l'abstraction et non sur la concrétion de modules de niveau inférieur. Mais la recherche des causes est plutôt une mise en œuvre de L'Injection de dépendance.

  • est-ce que le CIO rompt toujours encapsulation, et donc OOP?

    Je ne pense pas que le mécanisme du CIO violera L'Encapsulation. Mais peut rendre le système étroitement couplé.

  • le CIO devrait-il être utilisé avec parcimonie, religieusement ou de manière appropriée?

    IoC peut être utilisé comme a dans de nombreux modèles comme le modèle de pont, où séparer la concrétion de L'Abstraction améliore le code. Ainsi peut être utilisé pour obtenir la trempe.

  • Quelle est la différence entre le CIO et un conteneur CIO?

    IoC est un mécanisme D'Inversion de dépendance mais les conteneurs sont ceux qui utilisent IoC.

1
répondu Abdul Rahman K 2015-07-19 18:27:33

L'Encapsulation ne contredit pas les principes D'Inversion de dépendance dans le monde de programmation orienté objet. Par exemple, dans la conception d'une voiture, vous aurez un "moteur interne" qui sera encapsulé à partir du monde extérieur, et aussi des "roues" qui peuvent être remplacés facilement, et considéré comme élément extérieur de la voiture. La voiture a des spécifications (interface) pour tourner l'arbre des roues, et le composant des roues met en œuvre la partie qui interagit avec l'arbre.

Ici, Le le moteur interne représente le processus d'encapsulation, tandis que les composants de la roue représentent les principes D'Inversion de dépendance (DIP) dans la conception de la voiture. Avec DIP, Fondamentalement nous empêchons la construction d'un objet monolithique, et au lieu de cela nous rendons notre objet composable. Pouvez-vous imaginer que vous construisez une voiture, où vous ne pouvez pas remplacer les roues parce qu'elles sont intégrées dans la voiture.

vous pouvez aussi en savoir plus sur les principes D'Inversion de dépendance dans plus de détails sur mon blog Ici.

0
répondu kusnaditjung tjung 2016-06-09 15:32:50

Je ne vais répondre qu'à une seule question car beaucoup d'autres ont répondu à tout le reste. Et gardez à l'esprit, il n'y a pas de bonne ou mauvaise réponse, juste les préférences de l'utilisateur.

le CIO devrait-il être utilisé avec parcimonie, religieusement ou de manière appropriée? Mon expérience me porte à croire que L'Injection de dépendance ne devrait être utilisée que dans les classes générales qui pourraient avoir besoin d'être changées à l'avenir. L'utiliser religieusement conduira à certaines classes nécessitant 15 interfaces dans le constructeur qui peuvent vraiment beaucoup de temps. Cela tend à conduire à 20% de développement et 80% d'entretien de la maison.

Quelqu'un a apporté un exemple d'une voiture, et comment le constructeur de la voiture voulez changer les pneus. L'injection de dépendances permet de changer les pneus sans se soucier des détails spécifiques de la mise en œuvre. Mais si nous prenons l'injection de dépendance religieusement... Ensuite, nous devons aussi commencer à construire des interfaces avec les constituants des pneus... Eh bien, que sur les threads du pneu? Quel à propos de la couture dans les pneus? Et le produit chimique dans ces fils? Qu'en est-il des atomes dans ces produits chimiques? Etc... D'accord! Hey! À un moment donné, vous allez devoir dire "assez, c'est assez"! Ne transformons pas chaque petite chose en interface... Parce que ça peut prendre trop de temps. C'est normal que certaines classes soient auto-contenues et instanciées dans la classe elle-même! C'est plus rapide à développer, et instancier la classe est plus facile.

Juste mes 2 cents.

0
répondu Babak 2017-06-28 23:07:33

j'ai trouvé un cas où le CIO et l'injection de dépendance interrompent l'encapsulation . Supposons que nous ayons un cours de ListUtil . Dans cette classe, il y a une méthode appelée remove duplicates . Cette méthode accepte une Liste . Il y a une interface ISortAlgorith avec une méthode de tri . Il existe une classe appelée QuickSort qui implémente cette interface. Lorsque nous écrivons l'alogorithme pour supprimer les doublons, nous devons trier la liste à l'intérieur . Maintenant, si RemoveDuplicates permettent une interface ISortAlgorithm comme un paramètre(CIO/Dependency Injection) pour permettre l'extensibilité pour les autres à choisir un autre algorithme pour supprimer les doublons de nous exposer la complexité de supprimer les doublons fonction de la ListUtil classe . Violant ainsi la première pierre de Oups.

-1
répondu Rahul 2017-08-04 12:51:35