Hay tres formas comunes, AFAIK, de implementar la reutilización cuando se trata de OOP
- Herencia: generalmente para representar es una relación (un pato es un pájaro)
- Composición: generalmente para representar una relación tiene (un automóvil tiene un motor)
- Rasgos (por ejemplo, la palabra clave rasgo en PHP): ... no estoy muy seguro de esto
Si bien me parece que los rasgos pueden implementar relaciones has-a y is-a, no estoy realmente seguro de a qué tipo de modelado se destina. ¿Para qué tipo de situaciones se diseñaron los rasgos?
object-oriented-design
Extrakun
fuente
fuente
Respuestas:
Los rasgos son otra forma de hacer composición. Piense en ellos como una forma de componer todas las partes de una clase en tiempo de compilación (o tiempo de compilación JIT), ensamblando las implementaciones concretas de las partes que necesitará.
Básicamente, quieres usar rasgos cuando te encuentres haciendo clases con diferentes combinaciones de características. Esta situación se presenta con mayor frecuencia para las personas que escriben bibliotecas flexibles para que otras las consuman. Por ejemplo, aquí está la declaración de una clase de prueba unitaria que escribí recientemente usando ScalaTest :
Los marcos de prueba unitaria tienen un montón de opciones de configuración diferentes, y cada equipo tiene diferentes preferencias sobre cómo quieren hacer las cosas. Al poner las opciones en rasgos (que se mezclan al usar
with
en Scala), ScalaTest puede ofrecer todas esas opciones sin tener que crear nombres de clase comoWordSpecLikeWithMatchersAndFutures
, o un montón de banderas booleanas en tiempo de ejecución comoWordSpecLike(enableFutures, enableMatchers, ...)
. Esto facilita seguir el principio Abierto / cerrado . Puede agregar nuevas características y nuevas combinaciones de características, simplemente agregando un nuevo rasgo. También hace que sea más fácil seguir el Principio de segregación de interfaz , ya que puede colocar fácilmente funciones que no son universalmente necesarias en un rasgo.Los rasgos también son una buena forma de poner código común en varias clases que no tienen sentido compartir una jerarquía de herencia. La herencia es una relación muy estrecha, y no debe pagar ese costo si puede evitarlo. Los rasgos son una relación mucho más débilmente acoplada. En mi ejemplo anterior, solía
MyCustomTrait
compartir fácilmente una implementación de base de datos simulada entre varias clases de prueba que de otro modo no estarían relacionadas.La inyección de dependencia logra muchos de los mismos objetivos, pero en tiempo de ejecución basado en la entrada del usuario en lugar de en tiempo de compilación basado en la entrada del programador. Los rasgos también están destinados más a dependencias que son semánticamente parte de la misma clase. Es como reunir las partes de una clase en lugar de hacer llamadas a otras clases con otras responsabilidades.
Los marcos de inyección de dependencia logran muchos de los mismos objetivos en tiempo de compilación basados en la entrada del programador, pero son en gran medida una solución alternativa para los lenguajes de programación sin el soporte de rasgos adecuado. Los rasgos traen estas dependencias al dominio del verificador de tipo del compilador, con una sintaxis más limpia, con un proceso de compilación más simple, que hace una distinción más clara entre dependencias de tiempo de compilación y tiempo de ejecución.
fuente
with
operador es lo que los diferencia, ywith
es una operación de composición. Y sí, los rasgos reemplazan DI sin necesidad de definirse en el punto de entrada del código. Esa es una de las cosas que los hace preferibles en mi opinión.