¿Por qué muchos desarrolladores de software violan el principio de abrir / cerrar al modificar muchas cosas como renombrar funciones que romperán la aplicación después de la actualización?
Esta pregunta salta a mi cabeza después de las versiones rápidas y continuas en la biblioteca React .
Cada corto período noto muchos cambios en la sintaxis, nombres de componentes, ... etc.
Ejemplo en la próxima versión de React :
Nuevas advertencias de desaprobación
El mayor cambio es que hemos extraído React.PropTypes y React.createClass en sus propios paquetes. Aún se puede acceder a ambos a través del objeto React principal, pero el uso de ambos registrará una advertencia de desaprobación única en la consola cuando se encuentre en modo de desarrollo. Esto permitirá futuras optimizaciones de tamaño de código.
Estas advertencias no afectarán el comportamiento de su aplicación. Sin embargo, nos damos cuenta de que pueden causar cierta frustración, especialmente si utiliza un marco de prueba que trata la consola.error como un error.
- ¿Se consideran estos cambios como una violación de ese principio?
- Como principiante en algo como React , ¿cómo lo aprendo con estos rápidos cambios en la biblioteca (es muy frustrante)?
fuente
Respuestas:
En mi humilde opinión, la respuesta de JacquesB, aunque contiene mucha verdad, muestra un malentendido fundamental de la OCP. Para ser justos, su pregunta ya expresa este malentendido, también: cambiar el nombre de las funciones rompe la compatibilidad con versiones anteriores , pero no el OCP. Si parece necesario romper la compatibilidad (o mantener dos versiones del mismo componente para no romper la compatibilidad), ¡el OCP ya estaba roto antes!
Como Jörg W Mittag ya mencionó en sus comentarios, el principio no dice "no se puede modificar el comportamiento de un componente", dice, uno debe tratar de diseñar los componentes de una manera que estén abiertos para ser reutilizados (o extendidos) de varias maneras, sin necesidad de modificación. Esto se puede hacer proporcionando los "puntos de extensión" correctos o, como lo menciona @AntP, "descomponiendo una estructura de clase / función en el punto donde cada punto de extensión natural está allí por defecto". En mi humilde opinión, seguir el OCP no tiene nada en común con "mantener la versión anterior sin cambios para la compatibilidad con versiones anteriores" . O, citando el comentario de @ DerekElkin a continuación:
Los buenos programadores usan su experiencia para diseñar componentes con los puntos de extensión "correctos" en mente (o, mejor aún, de alguna manera no se necesitan puntos de extensión artificiales). Sin embargo, para hacer esto correctamente y sin una ingeniería excesiva innecesaria, debe saber de antemano cómo podrían ser los casos de uso futuros de su componente. Incluso los programadores experimentados no pueden mirar hacia el futuro y conocer de antemano todos los requisitos futuros. Y es por eso que a veces se debe violar la compatibilidad con versiones anteriores : no importa cuántos puntos de extensión tenga su componente o cuán bien siga el OCP con respecto a ciertos tipos de requisitos, siempre habrá un requisito que no se puede implementar fácilmente sin modificar el componente.
fuente
El principio abierto / cerrado tiene beneficios, pero también tiene algunos inconvenientes serios.
En teoría, el principio resuelve el problema de la compatibilidad con versiones anteriores mediante la creación de código que está "abierto para la extensión pero cerrado para la modificación". Si una clase tiene algunos requisitos nuevos, nunca modifica el código fuente de la clase en sí, sino que crea una subclase que anula solo los miembros apropiados necesarios para cambiar el comportamiento. Por lo tanto, todo el código escrito en la versión original de la clase no se ve afectado, por lo que puede estar seguro de que su cambio no rompió el código existente.
En realidad , fácilmente terminas con una gran cantidad de código y un lío confuso de clases obsoletas. Si no es posible modificar el comportamiento de un componente a través de la extensión, debe proporcionar una nueva variante del componente con el comportamiento deseado y mantener la versión anterior sin cambios para la compatibilidad con versiones anteriores.
Supongamos que descubre una falla de diseño fundamental en una clase base de la cual muchas clases heredan. Digamos que el error se debe a que un campo privado es del tipo incorrecto. No puede solucionar esto anulando un miembro. Básicamente, debe anular toda la clase, lo que significa que termina extendiéndose
Object
para proporcionar una clase base alternativa, y ahora también debe proporcionar alternativas a todas las subclases, lo que termina con una jerarquía de objetos duplicados, una jerarquía defectuosa, una mejorada . Pero no puede eliminar la jerarquía defectuosa (ya que la eliminación del código es una modificación), todos los clientes futuros estarán expuestos a ambas jerarquías.Ahora la respuesta teórica a este problema es "simplemente diseñarlo correctamente la primera vez". Si el código se descompone perfectamente, sin fallas ni errores, y está diseñado con puntos de extensión preparados para todos los posibles cambios de requisitos futuros, entonces evitará el desastre. Pero en realidad todos cometen errores y nadie puede predecir el futuro a la perfección.
Tomemos algo como el marco .NET: todavía contiene el conjunto de clases de colección que fueron diseñadas antes de que se introdujeran los genéricos hace más de una década. Esto es ciertamente una bendición para la compatibilidad con versiones anteriores (puede actualizar el marco sin tener que volver a escribir nada), pero también aumenta el marco y presenta a los desarrolladores un gran conjunto de opciones donde muchas simplemente son obsoletas.
Aparentemente, los desarrolladores de React han sentido que no valía la pena el costo en complejidad y en código inflado para seguir estrictamente el principio abierto / cerrado.
La alternativa pragmática a abrir / cerrar es la depreciación controlada. En lugar de romper la compatibilidad con versiones anteriores en una sola versión, los componentes antiguos se mantienen durante un ciclo de lanzamiento, pero los clientes son informados a través de advertencias del compilador de que el enfoque anterior se eliminará en una versión posterior. Esto les da a los clientes tiempo para modificar el código. Este parece ser el enfoque de React en este caso.
(Mi interpretación del principio se basa en El principio abierto-cerrado de Robert C. Martin)
fuente
Yo llamaría al principio abierto / cerrado un ideal. Como todos los ideales, da poca consideración a las realidades del desarrollo de software. También, como todos los ideales, es imposible alcanzarlo en la práctica: uno simplemente se esfuerza por alcanzar ese ideal lo mejor que puede.
El otro lado de la historia se conoce como las esposas doradas. Las esposas doradas son lo que obtienes cuando te esclavas demasiado al principio abierto / cerrado. Las esposas doradas son lo que ocurre cuando su producto que nunca rompe la compatibilidad con versiones anteriores no puede crecer porque se han cometido demasiados errores pasados.
Un famoso ejemplo de esto se encuentra en el administrador de memoria de Windows 95. Como parte de la comercialización de Windows 95, se afirmó que todas las aplicaciones de Windows 3.1 funcionarían en Windows 95. Microsoft adquirió licencias para miles de programas para probarlas en Windows 95. Uno de los casos problemáticos fue Sim City. Sim City en realidad tenía un error que hacía que escribiera en la memoria no asignada. En Windows 3.1, sin un administrador de memoria "adecuado", este fue un pequeño paso en falso. Sin embargo, en Windows 95, el administrador de memoria detectaría esto y causaría un error de segmentación. ¿La solución? En Windows 95, si el nombre de su aplicación es
simcity.exe
, ¡el sistema operativo realmente relajará las restricciones del administrador de memoria para evitar la falla de segmentación!El verdadero problema detrás de este ideal son los conceptos recortados de productos y servicios. Nadie realmente hace uno u otro. Todo se alinea en algún lugar de la región gris entre los dos. Si piensa desde un enfoque orientado al producto, abrir / cerrar suena como un gran ideal. Sus productos son confiables. Sin embargo, cuando se trata de servicios, la historia cambia. Es fácil demostrar que con el principio abierto / cerrado, la cantidad de funcionalidad que su equipo debe admitir debe acercarse asintóticamente al infinito, porque nunca puede limpiar la funcionalidad anterior. Esto significa que su equipo de desarrollo debe admitir más y más código cada año. Eventualmente llegas a un punto de ruptura.
La mayoría del software actual, especialmente el de código abierto, sigue una versión relajada común del principio abierto / cerrado. Es muy común ver abierto / cerrado seguido servilmente para lanzamientos menores, pero abandonado para lanzamientos mayores. Por ejemplo, Python 2.7 contiene muchas "malas elecciones" de los días Python 2.0 y 2.1, pero Python 3.0 los barrió a todos. (Además, el cambio del código base de Windows 95 con el código base de Windows NT cuando lanzaron Windows 2000 rompió todo tipo de cosas, pero no quiere decir que nunca tenemos que hacer frente a un administrador de memoria comprobar el nombre de la aplicación para decidir comportamiento!)
fuente
La respuesta de Doc Brown es más cercana a precisa, las otras respuestas ilustran malentendidos del Principio Abierto Cerrado.
Para articular de forma explícita el malentendido, parece que hay una creencia de que el OCP significa que no se debe hacer cambios hacia atrás incompatibles (o incluso cualquier cambios o algo en este sentido.) El OCP se trata de diseñar componentes de manera que usted no necesita a realizar cambios en ellos para ampliar su funcionalidad, independientemente de si esos cambios son compatibles con versiones anteriores o no. Hay muchas otras razones además de agregar funcionalidad para que pueda realizar cambios en un componente, ya sea que sean compatibles con versiones anteriores (por ejemplo, refactorización u optimización) o incompatibles con versiones anteriores (por ejemplo, depreciar y eliminar la funcionalidad). Que pueda hacer estos cambios no significa que su componente haya violado el OCP (y definitivamente no significa que usted están violando el OCP).
Realmente, no se trata del código fuente en absoluto. Una declaración más abstracta y relevante del OCP es: "un componente debe permitir la extensión sin necesidad de violar sus límites de abstracción". Yo iría más lejos y diría que una interpretación más moderna es: "un componente debería imponer sus límites de abstracción pero permitir la extensión". Incluso en el artículo sobre el OCP de Bob Martin mientras "describe" "cerrado a modificación" como "el código fuente es inviolable", más tarde comienza a hablar sobre la encapsulación que no tiene nada que ver con modificar el código fuente y todo que ver con la abstracción fronteras
Entonces, la premisa defectuosa en la pregunta es que el OCP es (destinado como) una guía sobre la evolución de una base de código. El OCP generalmente se eslogan como "un componente debe estar abierto a extensiones y cerrado a modificaciones por parte de los consumidores". Básicamente, si un consumidor de un componente desea agregar funcionalidad al componente, debería poder extender el componente antiguo a uno nuevo con la funcionalidad adicional, pero no debería poder cambiar el componente anterior.
El OCP no dice nada sobre el creador de un componente que cambia o elimina la funcionalidad. El OCP no recomienda mantener la compatibilidad de errores para siempre. Usted, como creador, no está violando el OCP al cambiar o incluso eliminar un componente. Usted, o más bien los componentes que ha escrito, están violando el OCP si la única forma en que los consumidores pueden agregar funcionalidad a sus componentes es mediante la mutación, por ejemplo, mediante parches de monoo tener acceso al código fuente y volver a compilar. En muchos casos, ninguna de estas opciones son para el consumidor, lo que significa que si su componente no está "abierto a la extensión" no tiene suerte. Simplemente no pueden usar su componente para sus necesidades. El OCP argumenta que no debe poner a los consumidores de su biblioteca en esta posición, al menos con respecto a alguna clase identificable de "extensiones". Incluso cuando se pueden hacer modificaciones al código fuente o incluso a la copia primaria del código fuente, es mejor "pretender" que no puede modificarlo, ya que existen muchas posibles consecuencias negativas.
Entonces, para responder a sus preguntas: No, estas no son violaciones del OCP. Ningún cambio que realice un autor puede ser una violación del OCP porque el OCP no es una proporción de los cambios. Sin embargo, los cambios pueden crear violaciones del OCP y pueden estar motivados por fallas del OCP en versiones anteriores de la base de código. El OCP es una propiedad de un código particular, no la historia evolutiva de una base de código.
Por el contrario, la compatibilidad con versiones anteriores es una propiedad de un cambio de código. No tiene sentido decir que algún código es o no compatible con versiones anteriores. Solo tiene sentido hablar sobre la compatibilidad con versiones anteriores de algunos códigos con respecto a algunos códigos más antiguos. Por lo tanto, nunca tiene sentido hablar de que el primer corte de algún código sea compatible o no con versiones anteriores. El primer corte de código puede satisfacer o no satisfacer el OCP, y en general podemos determinar si algún código satisface el OCP sin hacer referencia a ninguna versión histórica del código.
En cuanto a su última pregunta, podría decirse que está fuera de tema para StackExchange en general, ya que se basa principalmente en la opinión, pero en resumen es bienvenido a la tecnología y particularmente a JavaScript, donde en los últimos años el fenómeno que describe se ha llamado fatiga de JavaScript . (Siéntase libre de google para encontrar una variedad de otros artículos, algunos satíricos, que hablan de esto desde múltiples perspectivas).
fuente
private
o no. Si un autor hace unprivate
métodopublic
más adelante, eso no significa que haya violado el control de acceso, (1/2)private
antes. "Quitar un componente publicado es claramente un cambio radical", es una no secuencia. O los componentes de la nueva versión satisfacen el OCP o no, no necesita el historial de la base de código para determinar esto. Según su lógica, nunca podría escribir código que satisfaga el OCP. Está combinando compatibilidad con versiones anteriores, una propiedad de cambios de código, con el OCP, una propiedad de código. Su comentario tiene tanto sentido como decir que quicksort no es compatible con versiones anteriores. (2/2)