Uno de los principios de OOP que encontré es: -Encapsular lo que varía.
Entiendo cuál es el significado literal de la frase, es decir, ocultar lo que varía. Sin embargo, no sé cómo contribuiría exactamente a un mejor diseño. ¿Alguien puede explicarlo con un buen ejemplo?
design
design-patterns
object-oriented
encapsulation
Haris Ghauri
fuente
fuente
I don't know how exactly would it contribute to a better design
Los detalles de encapsulación tratan sobre el acoplamiento flojo entre el "modelo" y los detalles de implementación. Cuanto menos ligado esté el "modelo" a los detalles de implementación, más flexible será la solución. Y hace que sea más fácil evolucionarlo. "Resumen de los detalles".Respuestas:
Puede escribir código que se vea así:
o puedes escribir código que se vea así:
Si lo que varía está encapsulado, entonces no tiene que preocuparse por eso. Solo te preocupas por lo que necesitas y lo que sea que estés usando descubre cómo hacer lo que realmente necesitas según lo variado.
Encapsula lo que varía y no tienes que distribuir código que se preocupe por lo que varía. Simplemente configura la mascota como un cierto tipo que sabe hablar como ese tipo y luego puede olvidar qué tipo y tratarlo como una mascota. No tienes que preguntar qué tipo.
Puede pensar que el tipo está encapsulado porque se requiere un captador para acceder a él. Yo no. Getter realmente no encapsula. Simplemente chirrían cuando alguien rompe tu encapsulación. Son un buen decorador como un gancho orientado a aspectos que se usa con mayor frecuencia como código de depuración. No importa cómo lo corte, todavía está exponiendo el tipo.
Podrías mirar este ejemplo y pensar que estoy combinando polimorfismo y encapsulación. No soy. Estoy combinando "lo que varía" y "detalles".
El hecho de que tu mascota sea un perro es un detalle. Uno que puede variar para ti. Uno que tal vez no. Pero ciertamente uno que puede variar de persona a persona. A menos que creamos que este software solo será utilizado por los amantes de los perros, es inteligente tratar al perro como un detalle y encapsularlo. De esa forma, algunas partes del sistema desconocen felizmente al perro y no se verán afectadas cuando nos unamos con "los loros somos nosotros".
Desacoplar, separar y ocultar detalles del resto del código. No permita que el conocimiento de los detalles se extienda a través de su sistema y seguirá "encapsular lo que varía" perfectamente.
fuente
"Varía" aquí significa "puede cambiar con el tiempo debido a los requisitos cambiantes". Este es un principio de diseño central: separar y aislar piezas de código o datos que pueden tener que cambiar por separado en el futuro. Si cambia un solo requisito, idealmente debería exigirnos que cambiemos el código relacionado en un solo lugar. Pero si la base del código está mal diseñada, es decir, altamente interconectada y la lógica del requisito se extiende en muchos lugares, entonces el cambio será difícil y tendrá un alto riesgo de causar efectos inesperados.
Supongamos que tiene una aplicación que utiliza el cálculo del impuesto sobre las ventas en muchos lugares. Si la tasa del impuesto sobre las ventas cambia, ¿qué preferiría?
la tasa del impuesto sobre las ventas es un literal codificado en todas partes de la aplicación donde se calcula el impuesto sobre las ventas.
la tasa del impuesto sobre las ventas es una constante global, que se utiliza en todas partes en la aplicación donde se calcula el impuesto sobre las ventas.
Hay un único método llamado,
calculateSalesTax(product)
que es el único lugar donde se utiliza la tasa de impuesto a las ventas.la tasa de impuesto a las ventas se especifica en un archivo de configuración o campo de base de datos.
Dado que la tasa del impuesto sobre las ventas puede cambiar debido a una decisión política independiente de otros requisitos, preferimos tenerla aislada en una configuración, para que pueda cambiarse sin afectar ningún código. Pero también es concebible que la lógica para calcular el impuesto sobre las ventas pueda cambiar, por ejemplo, diferentes tasas para diferentes productos, por lo que también nos gusta encapsular la lógica de cálculo. La constante global puede parecer una buena idea, pero en realidad es mala, ya que puede alentar el uso del impuesto a las ventas en diferentes lugares del programa en lugar de en un solo lugar.
Ahora considere otra constante, Pi, que también se usa en muchos lugares del código. ¿Se cumple el mismo principio de diseño? No, porque Pi no va a cambiar. Extraerlo a un archivo de configuración o campo de base de datos simplemente introduce una complejidad innecesaria (y todo lo demás es igual, preferimos el código más simple). Tiene sentido convertirlo en una constante global en lugar de codificarlo en varios lugares para evitar inconsistencias y mejorar la legibilidad.
El punto es que si solo miramos cómo funciona el programa ahora , la tasa de impuesto a las ventas y Pi son equivalentes, ambos son constantes. Solo cuando consideramos lo que puede variar en el futuro , nos damos cuenta de que debemos tratarlos de manera diferente en el diseño.
Este principio es en realidad bastante profundo, porque significa que debe mirar más allá de lo que se supone que debe hacer la base de código hoy en día , y también considerar las fuerzas externas que pueden hacer que cambie, e incluso comprender a las diferentes partes interesadas detrás de los requisitos.
fuente
Ambas respuestas actuales parecen dar solo en el blanco, y se centran en ejemplos que nublan la idea central. Esto tampoco es (únicamente) un principio de OOP sino un principio de diseño de software en general.
Lo que "varía" en esta frase es el código. Christophe está a punto de decir que generalmente es algo que puede variar, es decir, a menudo lo anticipa . El objetivo es protegerse de futuros cambios en el código. Esto está estrechamente relacionado con la programación en una interfaz . Sin embargo, Christophe es incorrecto al limitar esto a "detalles de implementación". De hecho, el valor de este consejo a menudo se debe a cambios en los requisitos .
Esto solo está indirectamente relacionado con el estado de encapsulación, que es lo que creo que está pensando David Arno. Este consejo no siempre (pero a menudo lo hace) sugiere un estado encapsulado, y este consejo se aplica también a los objetos inmutables. De hecho, simplemente nombrar constantes es una forma (muy básica) de encapsular lo que varía.
CandiedOrange combina explícitamente "lo que varía" con "detalles". Esto es solo parcialmente correcto. Estoy de acuerdo en que cualquier código que varía es "detalles" en algún sentido, pero un "detalle" puede no variar (a menos que defina "detalles" para hacer esto tautológico). Puede haber razones para encapsular detalles que no varían, pero este dictamen no es uno. Hablando en términos generales, si estaba muy seguro de que "perro", "gato" y "pato" serían los únicos tipos con los que tendría que tratar, entonces este dictamen no sugiere la refactorización de CandiedOrange.
Casting del ejemplo de CandiedOrange en un contexto diferente, suponga que tenemos un lenguaje de procedimiento como C. Si tengo algún código que contenga:
Puedo esperar razonablemente que este código cambie en el futuro. Puedo "encapsularlo" simplemente definiendo un nuevo procedimiento:
y usar este nuevo procedimiento en lugar del bloque de código (es decir, una refactorización de "método de extracción") En este punto, agregar un tipo de "vaca" o lo que sea, solo requiere actualizar el
speak
procedimiento. Por supuesto, en un lenguaje OO, en su lugar, puede aprovechar el despacho dinámico como se alude a la respuesta de CandiedOrange. Esto sucederá naturalmente si accede apet
través de una interfaz. La eliminación de la lógica condicional a través del despacho dinámico es una preocupación ortogonal que fue parte de por qué hice esta representación procesal. También quiero enfatizar que esto no requiere características particulares de OOP. Incluso en un lenguaje OO, encapsular lo que varía no significa necesariamente que deba crearse una nueva clase o interfaz.Como un ejemplo más arquetípico (que está más cerca pero no del todo OO), digamos que queremos eliminar los duplicados de una lista. Digamos que lo implementamos iterando sobre la lista haciendo un seguimiento de los elementos que hemos visto hasta ahora en otra lista y eliminando cualquier elemento que hayamos visto. Es razonable suponer que es posible que queramos cambiar la forma en que hacemos un seguimiento de los elementos vistos por, al menos, razones de rendimiento. El dictamen para encapsular lo que varía sugiere que deberíamos construir un tipo de datos abstractos para representar el conjunto de elementos vistos. Nuestro algoritmo ahora se define en contra de este tipo de conjunto de datos abstracto, y si decidimos cambiar a un árbol de búsqueda binario, nuestro algoritmo no necesita cambiar ni importar. En un lenguaje OO, podemos usar una clase o interfaz para capturar este tipo de datos abstractos. En un lenguaje como SML / O '
Para un ejemplo basado en requisitos, supongamos que necesita validar algún campo con respecto a alguna lógica empresarial. Si bien es posible que ahora tenga requisitos específicos, sospecha que evolucionarán. Puede encapsular la lógica actual en su propio procedimiento / función / regla / clase.
Aunque esta es una preocupación ortogonal que no forma parte de "encapsular lo que varía", a menudo es natural abstraerse, es decir, parametrizarse por la lógica ahora encapsulada. Esto generalmente conduce a un código más flexible y permite cambiar la lógica al sustituir en una implementación alternativa en lugar de modificar la lógica encapsulada.
fuente
"Encapsular lo que varía" se refiere a la ocultación de los detalles de implementación que pueden cambiar y evolucionar.
Ejemplo:
Por ejemplo, supongamos que la clase
Course
realiza un seguimiento deStudents
que puede registrarse (). Puede implementarlo con aLinkedList
y exponer el contenedor para permitir la iteración en él:Pero esta no es una buena idea:
Si encapsulas lo que varía (o mejor dicho, lo que puede variar), mantienes la libertad para que tanto el código que usa como la clase encapsulada evolucionen entre sí por sí mismos. Por eso es un principio importante en OOP.
Lectura adicional:
fuente