Cómo cumplir con el principio abierto-cerrado en la práctica

13

Entiendo la intención del principio abierto-cerrado. Está pensado para reducir el riesgo de romper algo que ya funciona y al mismo tiempo modificarlo, diciéndole que intente extender sin modificar.

Sin embargo, tuve algunos problemas para entender cómo se aplica este principio en la práctica. A mi entender, hay dos maneras de aplicarlo. Antes y después de un posible cambio:

  1. Antes: programa a las abstracciones y 'predice el futuro' tanto como puedas. Por ejemplo, un método drive(Car car) tendrá que cambiar si Motorcycle s se agregan al sistema en el futuro, por lo que probablemente viola la OCP. Pero el método drive(MotorVehicle vehicle) es menor. Es probable que tenga que cambiar en el futuro, por lo que se adhiere a OCP.

    Sin embargo, es bastante difícil predecir el futuro y saber en Avance qué cambios se van a realizar en el sistema.

  2. Después: cuando se necesita un cambio, extienda una clase en lugar de modificarla código actual.

La práctica # 1 no es difícil de entender. Sin embargo, es la práctica número 2 que me cuesta entender cómo postular.

Por ejemplo (lo tomé de un video en YouTube): digamos que tenemos un método en una clase que acepta CreditCard objects: makePayment(CraditCard card) . Un día Voucher s se agregan al sistema. Este método no los admite, por lo que tiene que ser modificado.

Al implementar el método en primer lugar, no pudimos predecir el futuro y el programa en términos más abstractos (por ejemplo, makePayment(Payment pay) , por lo que ahora tenemos que cambiar el código existente.

La práctica n.º 2 dice que deberíamos agregar la funcionalidad extendiendo en lugar de modificar. ¿Qué significa eso? ¿Debería crear una subclase de la clase existente en lugar de simplemente cambiar su código existente? ¿Debería hacer algún tipo de envoltorio alrededor de él solo para evitar volver a escribir el código?

¿O el principio ni siquiera se refiere a 'cómo modificar / agregar correctamente la funcionalidad', sino a 'cómo evitar tener que hacer cambios en primer lugar (es decir, programar en abstracciones)?

    
pregunta Aviv Cohn 12.05.2014 - 15:35

3 respuestas

13

Los principios de diseño siempre deben equilibrarse unos con otros. No se puede predecir el futuro, y la mayoría de los programadores lo hacen horriblemente cuando lo intentan. Es por eso que tenemos la regla de tres , que trata principalmente sobre la duplicación, pero se aplica a la refactorización de cualquier otra principios de diseño también.

Cuando solo tienes una implementación de una interfaz, no tienes que preocuparte mucho por OCP, a menos que sea muy claro dónde se llevarán a cabo las extensiones. De hecho, a menudo pierde la claridad al intentar sobre-diseñar en esta situación. Cuando lo extiendes una vez, lo refactas para que sea amigable con OCP si esa es la forma más fácil y clara de hacerlo. Cuando lo extienda a una tercera implementación, asegúrese de refactorizarlo teniendo en cuenta OCP, incluso si requiere un poco más de esfuerzo.

En la práctica, cuando solo tiene dos implementaciones, refactorizar cuando agrega una tercera no suele ser demasiado difícil. Es cuando lo dejas crecer más allá de ese punto que se vuelve problemático de mantener.

    
respondido por el Karl Bielefeldt 12.05.2014 - 16:38
2

Creo que estás mirando demasiado lejos en el futuro. Resuelva el problema actual de una manera flexible que se adhiera a abrir / cerrar.

Supongamos que necesita implementar un método drive(Car car) . Dependiendo de tu idioma, tienes un par de opciones.

  • Para los idiomas que admiten la sobrecarga (C ++), simplemente use drive(const Car& car)

    En algún momento después, es posible que necesites drive(const Motorcycle& motorcycle) , pero no interferirá con drive(const Car& car) . No hay problema!

  • Para los idiomas que no admiten la sobrecarga (Objetivo C), incluya el nombre de tipo en el método -driveCar:(Car *)car .

    En algún momento después, es posible que necesites -driveMotorcycle:(Motorcycle *)motorcycle , pero de nuevo, no interferirá.

Esto permite que drive(Car car) se cierre a la modificación, pero está abierto a otros tipos de vehículos. Esta planificación minimalista de futuro que le permite realizar el trabajo hoy, pero le impide bloquearse en el futuro.

Tratar de imaginar los tipos más básicos que necesita puede llevar a una regresión infinita. ¿Qué sucede cuando quieres conducir un Segue, una bicicleta o un jumbo? ¿Cómo construye un único tipo de resumen genérico que puede explicar todos los dispositivos que las personas utilizan y utilizan para movilidad?     

respondido por el Jeffery Thomas 12.05.2014 - 17:23
2
  

Entiendo la intención del principio abierto-cerrado. Está pensado para reducir el riesgo de romper algo que ya funciona y al mismo tiempo modificarlo, diciéndole que intente extender sin modificar.

También se trata de no romper todos los objetos que dependen de ese método al no cambiar el comportamiento de los objetos ya existentes. Una vez que un objeto ha anunciado un cambio de comportamiento, es arriesgado, ya que está alterando el comportamiento conocido y esperado del objeto sin saber exactamente qué otros objetos esperan ese comportamiento.

  

¿Qué significa eso? ¿Debería subclasificar la clase existente en lugar de simplemente cambiar su código existente?

Sí.

"Sólo se aceptan tarjetas de crédito" se define como parte del comportamiento de esa clase, a través de su interfaz pública. El programador ha declarado al mundo que el método de este objeto solo acepta tarjetas de crédito. Lo hizo usando un nombre de método poco claro, pero está hecho. El resto del sistema está confiando en esto.

Eso podría haber tenido sentido en ese momento, pero ahora, si necesita cambiar ahora, debe crear una nueva clase que acepte otras cosas que no sean tarjetas de crédito.

Nuevo comportamiento = nueva clase

Como parte aparte : una buena manera de predecir el futuro es pensar en el nombre que le has dado a un método. ¿Le ha dado un nombre de método de sonido realmente general como makePayment a un método con reglas específicas en el método para determinar exactamente qué pago puede realizar? Eso es un olor de código. Si tiene reglas específicas, estas deben quedar claras en el nombre del método: makePayment debe ser makeCreditCardPayment. Haga esto cuando escriba el objeto la primera vez y otros programadores se lo agradecerán.

    
respondido por el Cormac Mulhall 22.05.2014 - 20:22

Lea otras preguntas en las etiquetas