En los lenguajes de programación orientados a objetos , un mixin (o mix-in ) [1] [2] [3] [4] es una clase que contiene métodos para ser usados por otras clases sin tener que ser la clase padre de esas otras clases. La forma en que esas otras clases acceden a los métodos del mixin depende del idioma. Los mixins a veces se describen como "incluidos" en lugar de "heredados".
Los mixins fomentan la reutilización del código y pueden usarse para evitar la ambigüedad de la herencia que la herencia múltiple puede causar [5] (el " problema del diamante "), o para evitar la falta de soporte para la herencia múltiple en un idioma. Un mixin también puede verse como una interfaz con métodos implementados . Este patrón es un ejemplo de cómo hacer cumplir el principio de inversión de dependencia .
Historia
Mixins apareció por primera vez en el sistema Flavors orientado a objetos de Symbolics (desarrollado por Howard Cannon), que era un enfoque de la orientación a objetos utilizado en Lisp Machine Lisp . El nombre se inspiró en Steve's Ice Cream Parlour en Somerville, Massachusetts: [1] El dueño de la heladería ofrecía un sabor básico de helado (vainilla, chocolate, etc.) y lo mezclaba en una combinación de elementos adicionales (nueces, cookies, dulce de azúcar, etc.) y llamó al artículo una " mezcla ", su propio término registrado en ese momento. [2]
Definición
Los mixins son un concepto de lenguaje que permite a un programador inyectar código en una clase . La programación mixta es un estilo de desarrollo de software , en el que se crean unidades de funcionalidad en una clase y luego se mezclan con otras clases. [6]
Una clase mixin actúa como clase principal, que contiene la funcionalidad deseada. Una subclase puede heredar o simplemente reutilizar esta funcionalidad, pero no como un medio de especialización. Normalmente, el mixin exportará la funcionalidad deseada a una clase secundaria , sin crear una relación rígida y única "es a". Aquí radica la diferencia importante entre los conceptos de mixins y herencia , en que la clase hija todavía puede heredar todas las características de la clase padre, pero la semántica sobre que el hijo "es una especie de" padre no tiene por qué aplicarse necesariamente.
Ventajas
- Proporciona un mecanismo para la herencia múltiple al permitir que una clase use la funcionalidad común de varias clases, pero sin la compleja semántica de la herencia múltiple. [7]
- Reutilización de código : los mixins son útiles cuando un programador desea compartir funcionalidades entre diferentes clases. En lugar de repetir el mismo código una y otra vez, la funcionalidad común puede simplemente agruparse en un mixin y luego incluirse en cada clase que lo requiera. [8]
- Los mixins permiten la herencia y el uso de solo las características deseadas de la clase principal, no necesariamente todas las características de la clase principal. [9]
Implementaciones
En Simula , las clases se definen en un bloque en el que los atributos, los métodos y la inicialización de la clase se definen todos juntos; por lo tanto, todos los métodos que se pueden invocar en una clase se definen juntos y la definición de la clase está completa.
En Flavours , un mixin es una clase de la cual otra clase puede heredar definiciones y métodos de ranuras. El mixin no suele tener instancias directas. Dado que un sabor puede heredar de más de otro sabor, puede heredar de uno o más mixins. Tenga en cuenta que los Flavours originales no usaban funciones genéricas.
En New Flavors (sucesor de Flavors) y CLOS , los métodos se organizan en " funciones genéricas ". Estas funciones genéricas son funciones que se definen en varios casos (métodos) mediante el envío de clases y combinaciones de métodos.
CLOS y Flavours permiten que los métodos mixin agreguen comportamiento a los métodos existentes: :before
y :after
demonios, whoppers y envoltorios en Flavours. CLOS agregó :around
métodos y la capacidad de llamar a métodos sombreados a través de CALL-NEXT-METHOD
. Entonces, por ejemplo, un stream-lock-mixin puede agregar un bloqueo alrededor de los métodos existentes de una clase de flujo. En Flavours se escribiría un envoltorio o un whopper y en CLOS se usaría un :around
método. Tanto CLOS como Flavours permiten la reutilización calculada mediante combinaciones de métodos. :before
, :after
y los :around
métodos son una característica de la combinación de métodos estándar. Se proporcionan otras combinaciones de métodos.
Un ejemplo es la +
combinación de métodos, donde los valores resultantes de cada uno de los métodos aplicables de una función genérica se suman aritméticamente para calcular el valor de retorno. Esto se usa, por ejemplo, con border-mixin para objetos gráficos. Un objeto gráfico puede tener una función de ancho genérica. El border-mixin agregaría un borde alrededor de un objeto y tiene un método que calcula su ancho. Una nueva clase bordered-button
(que es a la vez un objeto gráfico y usa el border
mixin) calcularía su ancho llamando a todos los métodos de ancho aplicables, a través de la +
combinación de métodos. Todos los valores devueltos se agregan y crean el ancho combinado del objeto.
En un artículo de OOPSLA 90, [10] Gilad Bracha y William Cook reinterpretan diferentes mecanismos de herencia encontrados en Smalltalk, Beta y CLOS como formas especiales de una herencia mixin.
Lenguajes de programación que usan mixins
Aparte de Flavours y CLOS (una parte de Common Lisp ), algunos lenguajes que usan mixins son:
- Ada (al extender un registro etiquetado existente con operaciones arbitrarias en un genérico)
- Cobra
- ColdFusion (basado en clases usando incluye y basado en objetos asignando métodos de un objeto a otro en tiempo de ejecución)
- Curl (con Curl RTE)
- D (llamado "mixins de plantilla" ; D también incluye una declaración "mixin" que compila cadenas como código).
- Dardo
- Factor [11]
- Groovy
- Delegación de JavaScript: funciones como roles (rasgos y combinaciones)
- Kotlin
- Menos
- OCaml
- Perl (a través de roles en la extensión Moose del sistema de objetos Perl 5)
- " Rasgos " de PHP
- Magik
- MATLAB [12]
- Pitón
- Raqueta ( documentación de mixins )
- Raku
- Rubí
- Scala [13]
- XOTcl / TclOO (sistemas de objetos integrados en Tcl ) [14]
- Sass (un lenguaje de hoja de estilo)
- Charla
- Vala
- Rápido
- SystemVerilog
- TypeScript ( documentación de mixins )
Algunos lenguajes no admiten mixins a nivel de lenguaje, pero pueden imitarlos fácilmente copiando métodos de un objeto a otro en tiempo de ejecución, "tomando prestados" los métodos del mixin. Esto también es posible con lenguajes de tipado estático , pero requiere la construcción de un nuevo objeto con el conjunto extendido de métodos.
Otros lenguajes que no admiten mixins pueden admitirlos de forma indirecta a través de construcciones de otros lenguajes. C # y Visual Basic .NET admiten la adición de métodos de extensión en interfaces, lo que significa que cualquier clase que implemente una interfaz con métodos de extensión definidos tendrá los métodos de extensión disponibles como pseudo-miembros.
Ejemplos de
En Common Lisp
Common Lisp proporciona mixins en CLOS (Common Lisp Object System) similar a Flavours.
object-width
es una función genérica con un argumento que usa la +
combinación de métodos. Esta combinación determina que se llamarán todos los métodos aplicables para una función genérica y se agregarán los resultados.
( defgeneric object-width ( objeto ) ( : combinación de método + ))
button
es una clase con un espacio para el texto del botón.
( botón defclass () (( texto : initform " haz clic en mí" )))
Existe un método para los objetos de la clase button que calcula el ancho en función de la longitud del texto del botón. +
es el calificador de método para la combinación de métodos del mismo nombre.
( defmethod object-width + (( botón de objeto )) ( * 10 ( length ( texto del objeto de valor de ranura ))))
Una border-mixin
clase. El nombramiento es solo una convención. No hay superclases ni ranuras.
( defclass border-mixin () ())
Existe un método que calcula el ancho del borde. Aquí son solo 4.
( defmethod object-width + (( object border-mixin )) 4 )
bordered-button
es una clase que hereda de ambos border-mixin
y button
.
( Defclass botón bordeado ( frontera-mixin botón ) ())
Ahora podemos calcular el ancho de un botón. La llamada object-width
calcula 80. El resultado es el resultado del único método aplicable: el método object-width
de la clase button
.
? ( ancho de objeto ( botón de creación de instancia )) 80
También podemos calcular el ancho de a bordered-button
. La llamada object-width
calcula 84. El resultado es la suma de los resultados de los dos métodos aplicables: el método object-width
de la clase button
y el método object-width
de la clase border-mixin
.
? ( ancho de objeto ( make-instance 'bordered-button )) 84
En Python
En Python , el SocketServer
módulo [15] tiene una UDPServer
clase y una TCPServer
clase. Actúan como servidores para servidores de socket UDP y TCP , respectivamente. Además, hay dos clases mixin: ForkingMixIn
y ThreadingMixIn
. Normalmente, todas las conexiones nuevas se manejan dentro del mismo proceso. Extendiendo TCPServer
con el ThreadingMixIn
siguiente:
clase ThreadingTCPServer ( ThreadingMixIn , TCPServer ): pasar
la ThreadingMixIn
clase agrega funcionalidad al servidor TCP de modo que cada nueva conexión crea un nuevo hilo . Usando el mismo método, ThreadingUDPServer
se puede crear sin tener que duplicar el código en formato ThreadingMixIn
. Alternativamente, el uso de ForkingMixIn
haría que el proceso se bifurcara para cada nueva conexión. Claramente, la funcionalidad para crear un nuevo hilo o bifurcar un proceso no es muy útil como clase independiente.
En este ejemplo de uso, los mixins proporcionan una funcionalidad subyacente alternativa sin afectar la funcionalidad como servidor de socket.
En rubí
La mayor parte del mundo de Ruby se basa en mixins vía Modules
. El concepto de mixins se implementa en Ruby mediante la palabra clave include
a la que le pasamos el nombre del módulo como parámetro .
Ejemplo:
clase del estudiante incluye Comparable # La clase Estudiante hereda el módulo Comparable a través del 'incluir' palabra clave attr_accessor : nombre , : Puntuación def initialize ( nombre , puntaje ) @name = nombre @score = puntaje final # Incluir el módulo Comparable requiere que la clase de implementación defina el operador de comparación <=> # Aquí está el operador de comparación. Comparamos 2 instancias de estudiantes en función de sus puntuaciones. def <=> ( otro ) @score <=> otro . puntaje final # Aquí está lo bueno: tengo acceso a <, <=,>,> = y otros métodos de la Interfaz Comparable de forma gratuita. finals1 = Estudiante . nuevo ( "Peter" , 100 ) s2 = Estudiante . nuevo ( "Jason" , 90 )s1 > s2 #verdadero s1 <= s2 #falso
En JavaScript
El objeto literal y el extend
enfoque
Es técnicamente posible agregar comportamiento a un objeto vinculando funciones a claves en el objeto. Sin embargo, esta falta de separación entre estado y comportamiento tiene inconvenientes:
- Entremezcla las propiedades del dominio del modelo con las del dominio de implementación.
- No compartir el comportamiento común. Los metaobjetos resuelven este problema separando las propiedades específicas de dominio de los objetos de sus propiedades específicas de comportamiento. [dieciséis]
Se utiliza una función de extensión para mezclar el comportamiento en: [17]
'uso estricto' ;const Halfling = función ( fName , lName ) { this . firstName = fName ; esto . lastName = lName ; };const mixin = { fullName () { devuelve esto . firstName + '' + esto . lastName ; }, renombrar ( primero , último ) { this . firstName = primero ; esto . lastName = last ; devuelve esto ; } };// Una función de extensión const extend = ( obj , mixin ) => { Object . llaves ( mixin ). forEach ( clave => obj [ clave ] = mixin [ clave ]); return obj ; };const sam = new Halfling ( 'Sam' , 'Loawry' ); const frodo = new Halfling ( 'Freeda' , 'Baggs' );// Mixin los otros métodos extend ( Halfling . Prototype , mixin );consola . log ( sam . fullName ()); // Sam Loawry consola . log ( frodo . fullName ()); // Freeda Baggssam . renombrar ( 'Samwise' , 'Gamgee' ); frodo . renombrar ( 'Frodo' , 'Bolsón' );consola . log ( sam . fullName ()); // Consola Samwise Gamgee . log ( frodo . fullName ()); // Frodo Bolsón
Mezclar con el uso de Object.assign ()
'uso estricto' ;// Creando un objeto const obj1 = { nombre : 'Marcus Aurelius' , ciudad : 'Roma' , nacido : '121-04-26' };// Mixin 1 const mix1 = { toString () { return ` $ { this . name } nació en $ { this . ciudad } en $ { esto . nacido } ` ; }, edad () { año constante = nueva fecha (). getFullYear (); const nacido = nueva Fecha ( este . nacido ). getFullYear (); año de regreso - nacido ; } }; // Mixin 2 const mix2 = { toString () { return ` $ { this . nombre } - $ { esto . ciudad } - $ { esto . nacido } ` ; } }; // Añadiendo los métodos de mixins al objeto usando Object.assign () Object . asignar ( obj1 , mix1 , mix2 );consola . log ( obj1 . toString ()); // Marcus Aurelius - Roma - Consola 121-04-26 . log ( `Su edad es $ { obj1 . age () } a partir de hoy` ); // Su edad es 1897 a día de hoy
El enfoque de combinación de vuelo basado en funciones pura y delegación
Aunque el enfoque descrito en primer lugar está muy extendido, el siguiente está más cerca de lo que ofrece fundamentalmente el núcleo del lenguaje de JavaScript: delegación .
Dos patrones basados en objetos de función ya funcionan sin la necesidad de la implementación de un tercero de extend
.
'uso estricto' ;// Implementación const EnumerableFirstLast = ( function () { // patrón de módulo basado en función. Const first = function () { return this [ 0 ]; }, last = function () { return this [ this . Length - 1 ]; } ; return function () { // mecánica basada en la función Flight-Mixin ... this . first = first ; // ... refiriéndose a ... this . last = last ; // ... código compartido. }; } ());// Aplicación - delegación explícita: // aplicando [primero] y [último] comportamiento enumerable en el [prototipo] de [Array]. EnumerableFirstLast . llamar ( Array . prototype );// Ahora puedes hacer: const a = [ 1 , 2 , 3 ]; a . primero (); // 1 a . último (); // 3
En otros idiomas
En el lenguaje de contenido web Curl , la herencia múltiple se usa ya que las clases sin instancias pueden implementar métodos. Los mixins comunes incluyen todos los ControlUI
s de apariencia heredados de SkinnableControlUI
objetos delegados de la interfaz de usuario que requieren menús desplegables heredados de StandardBaseDropdownUI y clases de mixin con nombres explícitos como FontGraphicMixin
, FontVisualMixin
y NumericAxisMixin-of
clase. La versión 7.0 agregó acceso a la biblioteca para que los mixins no tengan que estar en el mismo paquete o ser un resumen público. Los constructores de Curl son fábricas que facilitan el uso de herencia múltiple sin una declaración explícita de interfaces o mixins. [ cita requerida ]
Interfaces y rasgos
Java 8 introduce una nueva característica en forma de métodos predeterminados para interfaces. [18] Básicamente, permite definir un método en una interfaz con la aplicación en el escenario cuando se va a agregar un nuevo método a una interfaz después de que se realiza la configuración de programación de la clase de interfaz. Agregar una nueva función a la interfaz significa implementar el método en cada clase que usa la interfaz. Los métodos predeterminados ayudan en este caso donde se pueden introducir en una interfaz en cualquier momento y tienen una estructura implementada que luego es utilizada por las clases asociadas. Por lo tanto, los métodos predeterminados agregan la posibilidad de aplicar el concepto en una especie de mezcla.
Las interfaces combinadas con la programación orientada a aspectos también pueden producir mixins completos en lenguajes que admiten dichas características, como C # o Java. Además, mediante el uso del patrón de interfaz de marcador , programación genérica y métodos de extensión, C # 3.0 tiene la capacidad de imitar mixins. Con C # 3.0 vino la introducción de métodos de extensión y se pueden aplicar, no solo a clases, sino también a interfaces. Los métodos de extensión proporcionan funcionalidad adicional en una clase existente sin modificar la clase. Entonces es posible crear una clase auxiliar estática para una funcionalidad específica que define los métodos de extensión. Debido a que las clases implementan la interfaz (incluso si la interfaz real no contiene ningún método o propiedad para implementar), también recogerá todos los métodos de extensión. [3] [4] [19] C # 8.0 agrega la característica de métodos de interfaz predeterminados. [20]
ECMAScript (en la mayoría de los casos implementado como JavaScript) no necesita imitar la composición del objeto copiando campos paso a paso de un objeto a otro. Admite de forma nativa [21] la composición de objetos basada en Trait y mixin [22] [23] a través de objetos de función que implementan un comportamiento adicional y luego se delegan a través de call
oa apply
objetos que necesitan esta nueva funcionalidad.
En Scala
Scala tiene un sistema de tipos rico y los rasgos son parte de él, lo que ayuda a implementar el comportamiento de mezcla. Como su nombre lo revela, los Rasgos se usan generalmente para representar una característica o aspecto distinto que normalmente es ortogonal a la responsabilidad de un tipo concreto o al menos de una instancia determinada. [24] Por ejemplo, la capacidad de cantar se modela como una característica ortogonal: podría aplicarse a pájaros, personas, etc.
trait Singer { def sing { println ( "cantando…" ) } // más métodos }clase pájaro extiende cantante
Aquí, Bird ha mezclado todos los métodos del rasgo en su propia definición como si la clase Bird hubiera definido el método sing () por sí mismo.
Como extends
también se usa para heredar de una superclase, en caso de un rasgo extends
se usa si no se hereda ninguna superclase y solo para mezclar en el primer rasgo. Todos los siguientes rasgos se mezclan en el uso de palabras clave with
.
clase Persona clase Actor amplía Persona con cantante clase Actor amplía Cantante con intérprete
Scala permite mezclar un rasgo (creando un tipo anónimo ) al crear una nueva instancia de una clase. En el caso de una instancia de la clase Person, no todas las instancias pueden cantar. Esta función se utiliza entonces:
class Person { def tell { println ( "Human" ) } // más métodos }val cantandoPersona = nueva Persona con Cantante cantandoPersona . cantar
En Swift
Mixin se puede lograr en Swift mediante el uso de una función de idioma llamada implementación predeterminada en la extensión de protocolo.
Protocol ErrorDisplayable { error de func ( mensaje : String )}extensión ErrorDisplayable { func error ( mensaje : String ) { // Haz lo que sea necesario para mostrar un error // ... imprimir ( mensaje ) }}struct NetworkManager : ErrorDisplayable { func onError () { error ( "Compruebe su conexión a Internet" ) }}
Ver también
- Tipo abstracto
- Patrón de decorador
- Diseño basado en políticas
- Rasgo , una estructura similar que no requiere composición lineal
Referencias
- ^ a b Uso de mezclas con Python
- ^ a b Mix-Ins (helado de Steve, Boston, 1975) Archivado el 26 de octubre de 2007 en la Wayback Machine.
- ^ a b Implementación de mezclas con métodos de extensión de C #
- ^ a b Sé la respuesta (es 42): Mix-ins y C #
- ^ Boyland, John; Giuseppe Castagna (26 de junio de 1996). "Compilación de tipo seguro de especialización covariante: un caso práctico" . En Pierre Cointe (ed.). ECOOP '96, Programación orientada a objetos: X Conferencia Europea . Saltador. págs. 16-17. ISBN 9783540614395. Consultado el 17 de enero de 2014 .
- ^ http://c2.com/cgi/wiki?MixIn
- ^ http://culttt.com/2015/07/08/working-with-mixins-in-ruby/
- ^ http://naildrivin5.com/blog/2012/12/19/re-use-in-oo-inheritance.html
- ^ "Copia archivada" . Archivado desde el original el 25 de septiembre de 2015 . Consultado el 16 de septiembre de 2015 .CS1 maint: copia archivada como título ( enlace )
- ^ OOPSLA '90, herencia basada en Mixin (pdf)
- ^ slava (25 de enero de 2010). "Factor / Características / El idioma" . concatenative.org . Consultado el 15 de mayo de 2012 .
Principales características del lenguaje de Factor:… Sistema de objetos con herencia, funciones genéricas, envío de predicados y Mixins
Enlace externo en|publisher=
( ayuda ) - ^ https://in.mathworks.com/help/matlab/mixin-classes.html
- ^ "Composición de la clase Mixin" . École polytechnique fédérale de Lausanne . Consultado el 16 de mayo de 2014 .
- ^ Mixin clases en XOTcl
- ^ Código fuente para SocketServer en CPython 3.5
- ^ http://raganwald.com/2014/04/10/mixins-forwarding-delegation.html
- ^ "Copia archivada" . Archivado desde el original el 21 de septiembre de 2015 . Consultado el 16 de septiembre de 2015 .CS1 maint: copia archivada como título ( enlace )
- ^ https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
- ^ Mixins, genéricos y métodos de extensión en C #
- ^ Mezcle la funcionalidad al crear clases usando interfaces con métodos de interfaz predeterminados
- ^ Los muchos talentos de JavaScript para generalizar enfoques de programación orientada a roles como Traits y Mixins , 11 de abril de 2014.
- ^ Angus Croll, Una nueva mirada a JavaScript Mixins , publicado el 31 de mayo de 2011.
- ^ Patrones de reutilización de código JavaScript , 19 de abril de 2013.
- ^ https://gleichmann.wordpress.com/2009/07/19/scala-in-practice-traits-as-mixins-motivation
enlaces externos
- MixIn en el repositorio de patrones de Portland
- Mixins en ActionScript
- El sistema de objetos Common Lisp: una descripción de Richard P. Gabriel y Linda DeMichiel proporciona una buena introducción a la motivación para definir clases por medio de funciones genéricas.