2010-08-06 13 views
67

En java world (más precisamente si no tiene herencia múltiple/mixins) la regla de oro es bastante simple: "Favorecer la composición de objetos sobre la herencia de clase".Mixins vs composición en scala

Me gustaría saber si/cómo se cambia si también se consideran mixinas, especialmente en scala?
¿Las mezclas se consideran una forma de herencia múltiple o más composición de clase?
¿Existe también una pauta "Favorecer la composición del objeto sobre la composición de la clase" (o al revés)?

He visto algunos ejemplos cuando las personas usan (o abusan) mixins cuando la composición de objetos también puede hacer el trabajo y no siempre estoy seguro de cuál es mejor. Me parece que se puede lograr cosas muy similares con ellos, pero hay algunas diferencias también, algunos ejemplos:

  • visibilidad - con mixins todo se convierte en parte de la API pública, lo cual no es el caso de la composición.
  • nivel de detalle - en la mayoría de los casos mixins son menos detallado y un poco más fácil de usar, pero no es siempre el caso (por ejemplo, si también está usando tipos de auto en jerarquías complejas)

sé la respuesta corta es " Depende ", pero probablemente haya alguna situación típica cuando esto o aquello sea mejor.

Algunos ejemplos de directrices que pudiera surgir con el hasta ahora (suponiendo que tengo dos rasgos A y B y A desea utilizar algunos métodos de B):

  • Si desea ampliar la API de A con los métodos de B luego mixins, de lo contrario composición. Pero no ayuda si la clase/instancia que estoy creando no es parte de una API pública.
  • Si desea utilizar algunos patrones que necesitan mixins (por ejemplo, Stackable Trait Pattern), entonces es una decisión fácil.
  • Si tiene dependencias circulares, las mezclas con tipos propios pueden ayudar. (Intento evitar esta situación, pero no siempre es fácil)
  • Si quiere algunas decisiones dinámicas, en tiempo de ejecución, cómo hacer la composición y luego la composición del objeto.

En muchos casos mixins parece ser más fácil (y/o menos detallado), pero estoy bastante seguro de que también tienen algunos errores, como la "clase de Dios" y otros descritos en dos artículos Artima: part 1, part 2 (Por cierto, me parece que la mayoría de los otros problemas no son relevantes/no tan graves para scala).

¿Tiene más consejos como estos?

Respuesta

37

Muchos de los problemas que las personas tienen con las mezclas pueden evitarse en Scala si solo mezclas rasgos abstractos en las definiciones de clase y luego mezclas los rasgos concretos correspondientes en el momento de creación de instancias de objeto. Por ejemplo

trait Locking{ 
    // abstract locking trait, many possible definitions 
    protected def lock(body: =>A):A 
} 

class MyService{ 
    this:Locking => 
} 

//For this time, we'll use a java.util.concurrent lock 
val myService:MyService = new MyService with JDK15Locking 

Este constructo tiene varias cosas que recomendar. Primero, evita que haya una explosión de clases ya que se necesitan diferentes combinaciones de funcionalidades de rasgo. En segundo lugar, permite realizar pruebas fácilmente, ya que uno puede crear y mezclar rasgos concretos de "no hacer nada", similares a los objetos simulados. Finalmente, hemos ocultado por completo el rasgo de bloqueo utilizado, e incluso ese bloqueo, de los consumidores de nuestro servicio.

Como hemos superado la mayoría de los inconvenientes de las mezclas, aún nos queda una compensación entre la mezcla y la composición. Para mí, normalmente tomo la decisión en función de si un objeto delegado hipotético estaría encapsulado por completo por el objeto que lo contiene, o si podría compartirse potencialmente y tener un ciclo de vida propio. Locking proporciona un buen ejemplo de delegados completamente encapsulados. Si su clase utiliza un objeto de bloqueo para administrar el acceso simultáneo a su estado interno, ese bloqueo está completamente controlado por el objeto que lo contiene, y ni él ni sus operaciones se anuncian como parte de la interfaz pública de las clases. Para funcionalidad completamente encapsulada como esta, voy con mezclas. Para algo compartido, como un origen de datos, use composición.

+0

¿Cuál es el significado de la construcción 'this => Logging'? No compila – HRJ

+3

Es una anotación de self-type, que siempre olvido la sintaxis de. Editado En este caso, significa que cualquier objeto que extienda MyService también debe extenderse. –

10

Otras diferencias no se han mencionado:

  • clases rasgo no tienen ninguna existencia independiente:

(Programming Scala)

Si usted encuentra que un rasgo particular es se usa con más frecuencia como padre de otras clases, para que las clases secundarias se comporten como el rasgo principal, luego considere definir el rasgo como una clase en su lugar, para hacer que esta logica l relación más clara.
(Nos dicho comporta como, en lugar de es un, porque el primero es la definición más precisa de la herencia, basado en el principio de sustitución de liskov -. Ver [Martin2003], por ejemplo)

[Martin2003]: Robert C. Martin, desarrollo ágil de software: principios, patrones y prácticas, Prentice-Hall, 2003

  • mixins (trait) no tienen parámetros del constructor.

ahí la advice, still from Programming Scala:

Evitar campos concretos en los rasgos que no pueden ser inicializadas a valores por defecto adecuados.
Use campos abstractos en su lugar o convierta el rasgo en una clase con un constructor.
Por supuesto, los rasgos sin estado no tienen ningún problema con la inicialización.

Es un principio general de buen diseño orientado a objetos que una instancia siempre debe estar en un estado válido conocido, comenzando desde el momento en que finaliza el proceso de construcción.

Esa última parte, con respecto al estado inicial de un objeto, a menudo ha ayudado a decidir entre la clase (y la composición de clase) y el rasgo (y mixins) para un concepto dado.

+0

Gracias por su respuesta, pero creo que se trata más de rasgos vs clases. Me siento un poco más cómodo con ese tema :-) Tal vez no fui lo suficientemente preciso cuando usé el término "composición de la clase".Lo que quise decir se describe en el artículo Stackable Trait Pattern, "Este patrón es similar en estructura al patrón del decorador, excepto que involucra decoración con el propósito de la composición de clases en lugar de la composición del objeto", aunque mezcla clases y rasgos. –

+0

@Sandor: lo entiendo. Me dirigí a la parte básica de su pregunta, ya que todavía no me siento cómodo con la distinción, pero debería obtener respuestas más precisas pronto. – VonC

+0

Ok, gracias por su ayuda de todos modos. –