Escribí una respuesta previamente sobre el Principio Abierto-Cerrado (OCP) versus el Principio de Sustitución de Liskov (LSP) y ambos principios se relacionan mucho entre sí, pero aún son conceptualmente diferentes con algunos ejemplos artificiales de tener uno pero no el otro. Debido a esta respuesta, solo tocaré OCP brevemente y profundizaré en DIP y lo que hace que funcione.
Tratemos de discutir cómo OCP se relaciona y difiere con el Principio de Inversión de Dependencia (DIP) al explicar primero los diferentes principios.
Principio de inversión de dependencia
Al leer los Principios de OOD del tío Bob , encontrará que DIP establece lo siguiente:
Depende de abstracciones, no de concreciones.
Una abstracción en Java se logra simplemente con interface
y abstract
palabras clave, lo que significa que tiene un "contrato" para alguna entidad de software que el código debe seguir. Algunos lenguajes de programación no tienen la capacidad de establecer explícitamente comportamientos para que el código los siga, por lo que las abstracciones deben seguirse de manera más manual en lugar de que el compilador lo ayude a cumplir el contrato. Por ejemplo, en C ++ tienes clases con métodos virtuales y lenguajes de programación dinámicos como Javascript, debes asegurarte de usar los objetos de la misma manera (aunque en el caso de Javascript, esto se ha extendido en TypeScript que agrega un sistema de tipos para ayudarte con la escritura de contratos verificados por el compilador).
El nombre incluye el término "inversión" porque tradicionalmente (ya sabes en las viejas épocas oscuras de programación) escribiste estructuras de software que tenían módulos de nivel superior dependiendo de los módulos de nivel bajo. Por ejemplo, tenía sentido tener ButtonAtKitchen
entradas de manejo para a KitchenLamp1
y KitchenLamp2
. Desafortunadamente, eso hizo que el software fuera mucho más específico de lo necesario y el gráfico de objeto se vería así:
Entonces, cuando haces que el software sea más general, al agregar "contratos". Observe cómo las flechas en el gráfico de objetos "invierten" la dirección. Que las lámparas de cocina ahora dependen de a Button
. En otras palabras, los detalles ahora dependen de abstracciones en lugar de ser al revés.
Por lo tanto, tenemos una definición más general de DIP, también detallada en el artículo original de DIP por el tío Bob .
A. Los módulos de alto nivel no deberían depender de módulos de bajo nivel. Ambos deberían depender de la abstracción. B. Las abstracciones no deberían depender de los detalles. Los detalles deben depender de las abstracciones.
Principio abierto-cerrado
Continuando con los principios del tío Bob, encontrará que OCP establece lo siguiente:
Debería poder extender el comportamiento de una clase, sin modificarlo.
Un ejemplo de lograr esto es emplear el patrón de Estrategia donde una Context
clase está cerrada por modificaciones (es decir, no puede cambiar su código interno) pero también está abierta a la extensión a través de sus dependencias colaborativas (es decir, las clases de estrategia).
En un sentido más general, cualquier módulo está construido para ser extensible a través de sus puntos de extensión.
OCP es similar a DIP, ¿verdad?
No , en realidad no.
Aunque ambos están discutiendo abstracciones, son conceptualmente diferentes. Ambos principios analizan diferentes contextos, OCP en un módulo en particular y DIP en varios módulos. Puede lograr ambos al mismo tiempo que con la mayoría de los patrones de diseño de Gang of Four, pero aún puede alejarse del camino.
En el ejemplo DIP mencionado anteriormente, con el botón y las lámparas de cocina, ninguna de las lámparas de cocina es extensible (ni actualmente tiene ningún requisito que explique que deben serlo). El diseño está rompiendo OCP pero sigue DIP .
Un ejemplo inverso (y artificial) sería una lámpara de cocina extensible (con un punto de extensión similar a a LampShade
) pero el botón todavía depende de las lámparas . Está rompiendo DIP pero sigue a OCP .
No te preocupes, sucede
Esto es realmente algo que verás que sucede a menudo en el código de producción, que una parte puede romper un principio. En sistemas de software más grandes (es decir, algo más grande que los ejemplos anteriores), puede romper un principio pero manteniendo el otro generalmente porque necesita mantener el código simple. Esto, en mi opinión, está bien para módulos pequeños y autónomos, ya que están en el contexto relacionado con el Principio de Responsabilidad Única (SRP).
Una vez que algún módulo se complica, lo más probable es que tenga que mirarlo con todos los principios en mente y rediseñarlo o refactorizarlo a un patrón conocido.