Mixin - Mixin

Em linguagens de programação orientadas a objetos , um mixin (ou mix-in ) é uma classe que contém métodos para uso por outras classes sem ter que ser a classe pai dessas outras classes. Como essas outras classes obtêm acesso aos métodos do mixin depende do idioma. Mixins às vezes são descritos como sendo "incluídos" em vez de "herdados".

Mixins encorajam a reutilização de código e podem ser usados ​​para evitar a ambigüidade de herança que a herança múltipla pode causar (o " problema do diamante ") ou para contornar a falta de suporte para herança múltipla em uma linguagem. Um mixin também pode ser visto como uma interface com métodos implementados . Este padrão é um exemplo de aplicação do princípio de inversão de dependência .

História

Mixins apareceu pela primeira vez no sistema Flavors orientado a objetos do Symbolics (desenvolvido por Howard Cannon), que era uma abordagem para orientação a objetos usada em Lisp Machine Lisp . O nome foi inspirado na Sorveteria de Steve em Somerville, Massachusetts: O dono da sorveteria ofereceu um sabor básico de sorvete (baunilha, chocolate, etc.) e misturado em uma combinação de itens extras (nozes, biscoitos, doce de leite etc.) e chamou o item de " mix-in ", seu próprio termo de marca registrada na época.

Definição

Mixins são um conceito de linguagem que permite que um programador injete algum código em uma classe . A programação Mixin é um estilo de desenvolvimento de software , no qual as unidades de funcionalidade são criadas em uma classe e depois combinadas com outras classes.

Uma classe mixin atua como a classe pai, contendo a funcionalidade desejada. Uma subclasse pode então herdar ou simplesmente reutilizar essa funcionalidade, mas não como um meio de especialização. Normalmente, o mixin exportará a funcionalidade desejada para uma classe filha , sem criar um relacionamento rígido e único "é um". Aqui reside a diferença importante entre os conceitos de mixins e herança , em que a classe filha ainda pode herdar todos os recursos da classe pai, mas a semântica sobre o filho "ser uma espécie de" pai não precisa ser necessariamente aplicada.

Vantagens

  1. Ele fornece um mecanismo para herança múltipla , permitindo que uma classe use a funcionalidade comum de várias classes, mas sem a semântica complexa de herança múltipla.
  2. Reutilização de código : Mixins são úteis quando um programador deseja compartilhar funcionalidade entre classes diferentes. Em vez de repetir o mesmo código indefinidamente, a funcionalidade comum pode simplesmente ser agrupada em um mixin e então incluída em cada classe que a requer.
  3. Mixins permitem a herança e o uso de apenas os recursos desejados da classe pai, não necessariamente todos os recursos da classe pai.

Implementações

No Simula , as classes são definidas em um bloco no qual atributos, métodos e inicialização da classe são definidos juntos; assim, todos os métodos que podem ser chamados em uma classe são definidos juntos e a definição da classe está completa.

No Flavors , um mixin é uma classe da qual outra classe pode herdar definições e métodos de slot. O mixin geralmente não possui instâncias diretas. Já que um Flavor pode herdar de mais de um outro Flavor, ele pode herdar de um ou mais mixins. Observe que o Flavors original não usava funções genéricas.

Em New Flavors (um sucessor de Flavors) e CLOS , os métodos são organizados em " funções genéricas ". Essas funções genéricas são funções definidas em vários casos (métodos) por despacho de classe e combinações de métodos.

CLOS e Flavors permitem que métodos mixin adicionem comportamento a métodos existentes: :before e :after daemons, whoppers e wrappers em Flavors. CLOS adicionou :around métodos e a capacidade de chamar métodos ocultos via CALL-NEXT-METHOD . Assim, por exemplo, um stream-lock-mixin pode adicionar bloqueio em torno dos métodos existentes de uma classe de stream. Em Flavors, escrever-se-ia um wrapper ou whopper e em CLOS usaríamos um :around método. Tanto CLOS quanto Flavors permitem a reutilização computada por meio de combinações de métodos. :before , :after E :around os métodos são uma característica da combinação de métodos padrão. Outras combinações de métodos são fornecidas.

Um exemplo é a + combinação de métodos, onde os valores resultantes de cada um dos métodos aplicáveis ​​de uma função genérica são somados aritmeticamente para calcular o valor de retorno. Isso é usado, por exemplo, com o mixin de borda para objetos gráficos. Um objeto gráfico pode ter uma função de largura genérica. O border-mixin adicionaria uma borda ao redor de um objeto e tem um método que calcula sua largura. Uma nova classe bordered-button (que é um objeto gráfico e usa o border mixin) calcularia sua largura chamando todos os métodos de largura aplicáveis ​​- por meio da + combinação de métodos. Todos os valores de retorno são adicionados e criam a largura combinada do objeto.

Em um artigo OOPSLA 90, Gilad Bracha e William Cook reinterpretam diferentes mecanismos de herança encontrados em Smalltalk, Beta e CLOS como formas especiais de herança mixin.

Linguagens de programação que usam mixins

Além de Flavors e CLOS (uma parte do Common Lisp ), algumas linguagens que usam mixins são:

Algumas linguagens não suportam mixins no nível da linguagem, mas podem facilmente imitá-los copiando métodos de um objeto para outro em tempo de execução, "pegando emprestado" os métodos do mixin. Isso também é possível com linguagens tipadas estaticamente , mas requer a construção de um novo objeto com o conjunto estendido de métodos.

Outras linguagens que não suportam mixins podem suportá-los de forma circular por meio de outras construções de linguagem. C # e Visual Basic .NET oferecem suporte à adição de métodos de extensão em interfaces, o que significa que qualquer classe que implemente uma interface com métodos de extensão definidos terá os métodos de extensão disponíveis como pseudo-membros.

Exemplos

Em Lisp Comum

Common Lisp fornece mixins em CLOS (Common Lisp Object System) semelhante a Flavors.

object-width é uma função genérica com um argumento que usa a + combinação de métodos. Essa combinação determina que todos os métodos aplicáveis ​​para uma função genérica serão chamados e os resultados serão adicionados.

(defgeneric object-width (object)
  (:method-combination +))

button é uma classe com um slot para o texto do botão.

(defclass button ()
  ((text :initform "click me")))

Existe um método para objetos de botão de classe que calcula a largura com base no comprimento do texto do botão. + é o qualificador de método para a combinação de método do mesmo nome.

(defmethod object-width + ((object button))
   (* 10 (length (slot-value object 'text))))

Uma border-mixin aula. A nomenclatura é apenas uma convenção. Não existem superclasses e nem slots.

(defclass border-mixin () ())

Existe um método que calcula a largura da borda. Aqui são apenas 4.

(defmethod object-width + ((object border-mixin))
  4)

bordered-button é uma classe que herda de ambos border-mixin e button .

(defclass bordered-button (border-mixin button) ())

Agora podemos calcular a largura de um botão. A chamada object-width calcula 80. O resultado é o resultado do único método aplicável: o método object-width para a classe button .

? (object-width (make-instance 'button))
80

Também podemos calcular a largura de a bordered-button . A chamada object-width calcula 84. O resultado é a soma dos resultados dos dois métodos aplicáveis: o método object-width para a classe button e o método object-width para a classe border-mixin .

? (object-width (make-instance 'bordered-button))
84

Em Python

Em Python , o SocketServer módulo possui uma UDPServer classe e uma TCPServer classe. Eles atuam como servidores para servidores de soquete UDP e TCP , respectivamente. Além disso, existem duas classes de mixin: ForkingMixIn e ThreadingMixIn . Normalmente, todas as novas conexões são tratadas no mesmo processo. Ao estender TCPServer com o ThreadingMixIn seguinte:

class ThreadingTCPServer(ThreadingMixIn, TCPServer):
    pass

a ThreadingMixIn classe adiciona funcionalidade ao servidor TCP de forma que cada nova conexão crie um novo thread . Usando o mesmo método, um ThreadingUDPServer pode ser criado sem a necessidade de duplicar o código em ThreadingMixIn . Como alternativa, o uso de ForkingMixIn faria com que o processo fosse bifurcado para cada nova conexão. Claramente, a funcionalidade de criar um novo thread ou bifurcar um processo não é muito útil como uma classe autônoma.

Neste exemplo de uso, os mixins fornecem funcionalidade alternativa subjacente sem afetar a funcionalidade como um servidor de soquete.

Em Ruby

A maior parte do mundo Ruby é baseado em mixins via Modules . O conceito de mixins é implementado em Ruby pela palavra include - chave para a qual passamos o nome do módulo como parâmetro .

Exemplo:

class Student
  include Comparable # The class Student inherits the Comparable module using the 'include' keyword
  attr_accessor :name, :score

  def initialize(name, score)
    @name = name
    @score = score
  end

  # Including the Comparable module requires the implementing class to define the <=> comparison operator
  # Here's the comparison operator. We compare 2 student instances based on their scores.

  def <=>(other)
    @score <=> other.score
  end

  # Here's the good bit - I get access to <, <=, >,>= and other methods of the Comparable Interface for free.
end

s1 = Student.new("Peter", 100)
s2 = Student.new("Jason", 90)

s1 > s2 #true
s1 <= s2 #false

Em JavaScript

O Literal do Objeto e a extend Abordagem

É tecnicamente possível adicionar comportamento a um objeto vinculando funções a teclas no objeto. No entanto, essa falta de separação entre estado e comportamento tem desvantagens:

  1. Ele mistura as propriedades do domínio do modelo com as do domínio de implementação.
  2. Sem compartilhamento de comportamento comum. Os metaobjetos resolvem esse problema separando as propriedades específicas de domínio dos objetos de suas propriedades específicas de comportamento.

Uma função extend é usada para misturar o comportamento em:

'use strict';

const Halfling = function (fName, lName) {
  this.firstName = fName;
  this.lastName = lName;
};

const mixin = {
  fullName() {
    return this.firstName + ' ' + this.lastName;
  },
  rename(first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
};

// An extend function
const extend = (obj, mixin) => {
  Object.keys(mixin).forEach(key => obj[key] = mixin[key]);
  return obj;
};

const sam = new Halfling('Sam', 'Loawry');
const frodo = new Halfling('Freeda', 'Baggs');

// Mixin the other methods
extend(Halfling.prototype, mixin);

console.log(sam.fullName());  // Sam Loawry
console.log(frodo.fullName());  // Freeda Baggs

sam.rename('Samwise', 'Gamgee');
frodo.rename('Frodo', 'Baggins');

console.log(sam.fullName());  // Samwise Gamgee
console.log(frodo.fullName());  // Frodo Baggins

Mixin com o uso de Object.assign ()

'use strict';

// Creating an object
const obj1 = {
  name: 'Marcus Aurelius',
  city: 'Rome',
  born: '121-04-26'
};

// Mixin 1
const mix1 = {
  toString() {
    return `${this.name} was born in ${this.city} in ${this.born}`;
  },
  age() {
    const year = new Date().getFullYear();
    const born = new Date(this.born).getFullYear();
    return year - born;
  }
};
// Mixin 2
const mix2 = {
  toString() {
    return `${this.name} - ${this.city} - ${this.born}`;
  }
};

//  Adding the methods from mixins to the object using Object.assign()
Object.assign(obj1, mix1, mix2);

console.log(obj1.toString());   // Marcus Aurelius - Rome - 121-04-26
console.log(`His age is ${obj1.age()} as of today`);  // His age is 1897 as of today

A abordagem Flight-Mixin baseada em função pura e delegação

Embora a primeira abordagem descrita seja amplamente difundida, a próxima está mais próxima do que o núcleo da linguagem JavaScript oferece fundamentalmente - Delegação .

Dois padrões baseados em objetos de função já funcionam sem a necessidade de implementação de terceiros extend .

'use strict';

// Implementation
const EnumerableFirstLast = (function () { // function based module pattern.
  const first = function () {
      return this[0];
    },
    last = function () {
      return this[this.length - 1];
    };
  return function () {      // function based Flight-Mixin mechanics ...
    this.first  = first;  // ... referring to ...
    this.last   = last;   // ... shared code.
  };
}());

// Application - explicit delegation:
// applying [first] and [last] enumerable behavior onto [Array]'s [prototype].
EnumerableFirstLast.call(Array.prototype);

// Now you can do:
const a = [1, 2, 3];
a.first(); // 1
a.last();  // 3

Em outras línguas

Na linguagem de conteúdo da web Curl , a herança múltipla é usada porque as classes sem instâncias podem implementar métodos. Os mixins comuns incluem todos os skinnable ControlUI s herdados de SkinnableControlUI objetos delegados da interface do usuário que requerem menus suspensos herdados de StandardBaseDropdownUI e tais classes mixin explicitamente nomeadas como FontGraphicMixin , FontVisualMixin e NumericAxisMixin-of class. A versão 7.0 adicionou acesso à biblioteca para que os mixins não precisem estar no mesmo pacote ou ser um resumo público. Os construtores Curl são fábricas que facilitam o uso de herança múltipla sem declaração explícita de interfaces ou mixins.

Interfaces e traços

Java 8 apresenta um novo recurso na forma de métodos padrão para interfaces. Basicamente, ele permite que um método seja definido em uma interface com a aplicação no cenário em que um novo método deve ser adicionado a uma interface após a configuração da programação da classe de interface. Adicionar uma nova função à interface significa implementar o método em todas as classes que usam a interface. Os métodos padrão ajudam neste caso, onde eles podem ser introduzidos em uma interface a qualquer momento e têm uma estrutura implementada que é então usada pelas classes associadas. Conseqüentemente, os métodos padrão adicionam a possibilidade de aplicar o conceito de uma forma mixin.

As interfaces combinadas com a programação orientada a aspectos também podem produzir mixins completos em linguagens que oferecem suporte a tais recursos, como C # ou Java. Além disso, por meio do uso do padrão de interface de marcador , programação genérica e métodos de extensão, o C # 3.0 tem a capacidade de imitar mixins. Com o C # 3.0 veio a introdução dos métodos de extensão e eles podem ser aplicados, não apenas às classes, mas, também, às interfaces. Métodos de extensão fornecem funcionalidade adicional em uma classe existente sem modificar a classe. Em seguida, torna-se possível criar uma classe auxiliar estática para funcionalidades específicas que definem os métodos de extensão. Como as classes implementam a interface (mesmo que a interface real não contenha nenhum método ou propriedade a ser implementada), ela também selecionará todos os métodos de extensão. C # 8.0 adiciona o recurso de métodos de interface padrão.

ECMAScript (na maioria dos casos implementado como JavaScript) não precisa imitar a composição do objeto copiando os campos passo a passo de um objeto para outro. Ele oferece suporte nativo à composição de objetos baseada em Traço e mixin por meio de objetos de função que implementam comportamento adicional e, em seguida, são delegados por meio de call ou apply para objetos que precisam dessa nova funcionalidade.

Em Scala

Scala tem um sistema de tipos rico e Traits são uma parte dele, o que ajuda a implementar o comportamento mixin. Como seu nome revela, Traits são geralmente usados ​​para representar uma característica ou aspecto distinto que normalmente é ortogonal à responsabilidade de um tipo concreto ou pelo menos de uma determinada instância. Por exemplo, a capacidade de cantar é modelada como uma característica ortogonal: pode ser aplicada a pássaros, pessoas, etc.

trait Singer{
  def sing { println(" singing … ") }
  //more methods
}

class Bird extends Singer

Aqui, Bird combinou todos os métodos do traço em sua própria definição, como se a classe Bird tivesse definido o método sing () por conta própria.

Como extends também é usado para herdar de uma superclasse, no caso de uma característica extends é usada se nenhuma superclasse for herdada e apenas para mixin na primeira característica. Todas as características a seguir são misturadas com o uso de palavras-chave with .

class Person
class Actor extends Person with Singer
class Actor extends Singer with Performer

Scala permite misturar uma característica (criando um tipo anônimo ) ao criar uma nova instância de uma classe. No caso de uma instância da classe Person, nem todas as instâncias podem cantar. Este recurso vem então para uso:

class Person{
  def tell {  println (" Human ") }
  //more methods
}

val singingPerson = new Person with Singer
singingPerson.sing

In Swift

O Mixin pode ser obtido no Swift usando um recurso de linguagem chamado Implementação padrão na extensão de protocolo.

protocol ErrorDisplayable {
    func error(message:String)
}

extension ErrorDisplayable {
    func error(message:String) {
        // Do what it needs to show an error
        //...
        print(message)
    }
}

struct NetworkManager : ErrorDisplayable {
    func onError() {
        error("Please check your internet Connection.")
    }
}

Veja também

Referências

links externos