Herança múltipla

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar
Em software, para ter várias classes pai

Herança múltipla é um recurso de algumas linguagens de programação de computador orientadas a objetos em que um objeto ou classe pode herdar recursos de mais de um objeto pai ou classe pai. É diferente da herança única, onde um objeto ou classe só pode herdar de um determinado objeto ou classe.

A herança múltipla tem sido um assunto controverso por muitos anos, com oponentes apontando para sua maior complexidade e ambigüidade em situações como o "problema do diamante", onde pode ser ambíguo quanto a qual classe pai um determinado O recurso é herdado se mais de uma classe pai implementa o referido recurso. Isso pode ser resolvido de várias maneiras, incluindo o uso de herança virtual. Métodos alternativos de composição de objetos não baseados em herança, como mixins e traits, também foram propostos para resolver a ambigüidade.

Detalhes

Na programação orientada a objetos (OOP), herança descreve um relacionamento entre duas classes em que uma classe (a classe filho) subclasses a classe pai. O filho herda métodos e atributos do pai, permitindo a funcionalidade compartilhada. Por exemplo, pode-se criar uma classe variável Mamífero com características como comer, reproduzir, etc.; em seguida, defina uma classe filha Gato que herda esses recursos sem ter que programá-los explicitamente, enquanto adiciona novos recursos como perseguir ratos.

A herança múltipla permite que os programadores usem mais de uma hierarquia totalmente ortogonal simultaneamente, como permitir que Gato herde de Personagem de desenho animado e Pet e Mamífero e acessar recursos de todas essas classes.

Implementações

As linguagens que suportam herança múltipla incluem: C++, Common Lisp (via Common Lisp Object System (CLOS)), EuLisp (via The EuLisp Object System TELOS), Curl, Dylan, Eiffel, Logtalk, Object REXX, Scala (via use de classes mixin), OCaml, Perl, POP-11, Python, R, Raku e Tcl (integrado a partir do 8.6 ou via Incremental Tcl (Incr Tcl) em versões anteriores).

O tempo de execução do IBM System Object Model (SOM) suporta herança múltipla e qualquer linguagem de programação destinada ao SOM pode implementar novas classes SOM herdadas de várias bases.

Algumas linguagens orientadas a objetos, como Swift, Java, Fortran desde sua revisão de 2003, C# e Ruby implementam herança única, embora protocolos ou interfaces forneçam algumas das funcionalidades da verdadeira herança múltipla.

O PHP usa classes de características para herdar implementações de métodos específicos. Ruby usa módulos para herdar vários métodos.

O problema do diamante

Um diagrama de herança de classe de diamante.

O "problema do diamante" (às vezes referido como o "Diamante Mortal da Morte") é uma ambigüidade que surge quando duas classes B e C herdam de A, e a classe D herda de B e C. Se houver um método em A que B e C foram substituídos e D não o substitui, qual versão do método D herda: a de B ou a de C?

Por exemplo, no contexto de desenvolvimento de software GUI, uma classe Button pode herdar de ambas as classes Rectangle (para aparência) e Clicável (para manipulação de funcionalidade/entrada) e as classes Rectangle e Clicável herdam da classe Object. Agora, se o método equals for chamado para um objeto Button e não houver tal método na classe Button, mas houver um substituído equals método em Rectangle ou Clicável (ou ambos), qual método deve ser eventualmente chamado?

É chamado de "problema do diamante" devido ao formato do diagrama de herança de classe nessa situação. Nesse caso, a classe A está no topo, tanto B quanto C separadamente abaixo dela, e D une os dois na parte inferior para formar uma forma de diamante.

Mitigação

As linguagens têm maneiras diferentes de lidar com esses problemas de herança repetida.

  • C# (desde C# 8.0) permite a implementação do método de interface padrão, causando uma classe A, interfaces de implementação Ia e Ib com métodos semelhantes com implementações padrão, para ter dois métodos "herdados" com a mesma assinatura, causando o problema do diamante. É mitigado tanto por ter A para implementar o próprio método, portanto, remover a ambiguidade, ou forçar o chamador a lançar primeiro o A objeto para a interface apropriada para usar sua implementação padrão desse método (por exemplo. ((Ia) aInstance).Method();).
  • C++ por padrão segue cada caminho de herança separadamente, então um D objeto realmente conter dois separados A objetos e usos de AOs membros têm de ser devidamente qualificados. Se a herança de A para B e a herança de A para C são ambos marcados "virtual" (por exemplo, "class B: virtual public A"), C++ toma cuidado especial para apenas criar um A objeto e usos de AOs membros funcionam corretamente. Se a herança virtual e a herança não virtual são misturadas, há um único virtual A, e A para cada caminho de herança não virtual A. C++ exige declarar explicitamente qual classe pai o recurso a ser usado é invocado a partir de i.e. Worker::Human.Age. C++ não suporta herança repetida explícita, uma vez que não haveria nenhuma maneira de qualificar qual superclasse usar (ou seja, ter uma classe aparece mais de uma vez em uma única lista de derivação [class Dog: public Animal, Animal]). C++ também permite que uma única instância da classe múltipla seja criada através do mecanismo de herança virtual (i.e. Worker::Human e Musician::Human irá referenciar o mesmo objeto).
  • Common Lisp CLOS tenta fornecer comportamento padrão razoável e a capacidade de substituí-lo. Por padrão, para colocá-lo simplesmente, os métodos são classificados em D,B,C,A, quando B é escrito antes de C na definição de classe. O método com as classes de argumento mais específicas é escolhido (D>(B,C)>A); então na ordem em que as classes-mãe são nomeadas na definição de subclasse (B>C). No entanto, o programador pode substituir isso, dando uma ordem específica de resolução de métodos ou afirmando uma regra para combinar métodos. Isto é chamado combinação de método, que pode ser totalmente controlado. O MOP (protocolo metaobject) também fornece meios para modificar a herança, despacho dinâmico, instantâneo de classe e outros mecanismos internos sem afetar a estabilidade do sistema.
  • Curl permite apenas classes que são explicitamente marcadas como compartilhado ser herdado repetidamente. As classes compartilhadas devem definir uma construtor secundário para cada construtor regular na classe. O construtor regular é chamado pela primeira vez que o estado para a classe compartilhada é inicializado através de um construtor subclasse, e o construtor secundário será invocado para todas as outras subclasses.
  • Em Eiffel, as características dos ancestrais são escolhidas explicitamente com diretivas de seleção e renomeação. Isso permite que as características da classe base sejam compartilhadas entre seus descendentes ou para dar a cada um deles uma cópia separada da classe base. Eiffel permite a união explícita ou separação de características herdadas de classes ancestrais. Eiffel vai automaticamente juntar características, se eles têm o mesmo nome e implementação. O escritor de classe tem a opção de renomear os recursos herdados para separá-los. A herança múltipla é uma ocorrência frequente no desenvolvimento Eiffel; a maioria das classes eficazes na biblioteca amplamente utilizada EiffelBase de estruturas de dados e algoritmos, por exemplo, têm dois ou mais pais.
  • Ir evita o problema do diamante em tempo de compilação. Se uma estrutura D incorpora duas estruturas B e C que ambos têm um método F(), satisfazendo assim uma interface A, o compilador vai reclamar sobre um "seletor ambíguo" se D.F() é chamado, ou se uma instância de D é atribuído a uma variável de tipo A. B e COs métodos podem ser chamados explicitamente com D.B.F() ou D.C.F().
  • O Java 8 introduz métodos padrão em interfaces. Se A,B,C são interfaces, B,C pode cada um fornecer uma implementação diferente para um método abstrato de A, causando o problema do diamante. Qualquer classe D deve reexaminar o método (o corpo do qual pode simplesmente encaminhar a chamada para uma das super implementações), ou a ambiguidade será rejeitada como um erro de compilação. Antes do Java 8, Java não estava sujeito ao risco de problema Diamond, porque não suportava vários métodos padrão de herança e interface não estavam disponíveis.
  • JavaFX Script na versão 1.2 permite múltiplas heranças através do uso de mixinas. Em caso de conflito, o compilador proíbe o uso direto da variável ou função ambígua. Cada membro herdado ainda pode ser acessado por lançar o objeto para a mistura de interesse, por exemplo. (individual as Person).printInfo();.
  • Kotlin permite herança múltipla de Interfaces, no entanto, em um cenário de problema Diamond, a classe infantil deve substituir o método que causa o conflito de herança e especificar qual a implementação da classe pai deve ser usada. eg super.someMethod()
  • Logtalk suporta interface e implementação multi-herança, permitindo a declaração de método aliases que fornecem renomeação e acesso a métodos que seriam mascarados pelo mecanismo de resolução de conflitos padrão.
  • Em OCaml, as classes-mãe são especificadas individualmente no corpo da definição de classe. Métodos (e atributos) são herdados na mesma ordem, com cada método recém- herdado substituindo quaisquer métodos existentes. OCaml escolhe a última definição de correspondência de uma lista de herança de classe para resolver qual implementação de método para usar sob ambiguidades. Para substituir o comportamento padrão, basta qualificar uma chamada de método com a definição de classe desejada.
  • Perl usa a lista de classes para herdar como uma lista ordenada. O compilador usa o primeiro método que ele encontra por pesquisa profunda da lista de superclasses ou usando a linearização C3 da hierarquia de classes. Várias extensões fornecem esquemas alternativos de composição de classe. A ordem de herança afeta a semântica de classe. Na ambiguidade acima, classe B e seus ancestrais seriam verificados antes da classe C e seus ancestrais, assim o método em A seria herdado através B. Isso é compartilhado com Io e Picolisp. Em Perl, esse comportamento pode ser substituído usando o mro ou outros módulos para usar a linearização C3 ou outros algoritmos.
  • Python tem a mesma estrutura que Perl, mas, ao contrário de Perl, inclui-a na sintaxe da linguagem. A ordem de herança afeta a semântica de classe. Python teve que lidar com isso após a introdução de novas classes de estilo, todas com um ancestral comum, object. Python cria uma lista de classes usando o algoritmo C3 (ou Method Resolution Order (MRO). Esse algoritmo aplica duas restrições: as crianças precedem seus pais e se uma classe herda de várias classes, elas são mantidas na ordem especificada no tupla de classes básicas (no entanto, neste caso, algumas classes altas no grafo de herança podem preceder as classes mais baixas no grafo). Assim, a ordem de resolução do método é: D, B, C, A.
  • Aulas de Ruby têm exatamente um pai, mas também podem herdar de vários módulos; definições de classe ruby são executadas, e a (re)definição de um método obscurece qualquer definição existente no momento da execução. Na ausência de metaprogramação em tempo de execução, isso tem aproximadamente a mesma semântica que a primeira resolução de profundidade mais correta.
  • Scala permite múltiplas instantâneas características, que permite a herança múltipla, adicionando uma distinção entre a hierarquia de classe e a hierarquia de traços. Uma classe só pode herdar de uma única classe, mas pode misturar-se como muitos traços como desejado. Scala resolve os nomes dos métodos usando uma primeira busca de profundidade certa de 'traits' estendidos, antes de eliminar tudo, mas a última ocorrência de cada módulo na lista resultante. Assim, a ordem de resolução é: [D, C, A, B, AO que reduz para baixo para [D, C, B, A].
  • Tcl permite várias classes parentais; a ordem de especificação na declaração de classe afeta a resolução de nome para membros usando o algoritmo de linearização C3.

Linguagens que permitem apenas herança única, onde uma classe só pode derivar de uma classe base, não têm o problema do diamante. A razão para isso é que tais linguagens têm no máximo uma implementação de qualquer método em qualquer nível na cadeia de herança, independentemente da repetição ou colocação de métodos. Normalmente, essas linguagens permitem que as classes implementem vários protocolos, chamados de interfaces em Java. Esses protocolos definem métodos, mas não fornecem implementações concretas. Essa estratégia tem sido usada por ActionScript, C#, D, Java, Nemerle, Object Pascal, Objective-C, Smalltalk, Swift e PHP. Todas essas linguagens permitem que as classes implementem vários protocolos.

Além disso, Ada, C#, Java, Object Pascal, Objective-C, Swift e PHP permitem herança múltipla de interfaces (chamadas de protocolos em Objective-C e Swift). As interfaces são como classes base abstratas que especificam assinaturas de método sem implementar nenhum comportamento. (Interfaces "Puras" como as do Java até a versão 7 não permitem nenhuma implementação ou dados de instância na interface.) No entanto, mesmo quando várias interfaces declaram a mesma assinatura de método, assim que esse método for implementado (definido) em qualquer lugar na cadeia de herança, ele substitui qualquer implementação desse método na cadeia acima dele (em suas superclasses). Portanto, em qualquer nível da cadeia de herança, pode haver no máximo uma implementação de qualquer método. Assim, a implementação do método de herança única não exibe o Problema do Diamante, mesmo com herança múltipla de interfaces. Com a introdução da implementação padrão para interfaces em Java 8 e C# 8, ainda é possível gerar um Diamond Problem, embora isso apareça apenas como um erro de tempo de compilação.

Contenido relacionado

Bill Joy

William Nelson Joy é um engenheiro de computação e capitalista de risco americano. Ele cofundou a Sun Microsystems em 1982 junto com Scott McNealy, Vinod...

Quilobyte

O quilobyte é um múltiplo do byte unitário para informações...

HMAC

Na criptografia, um HMAC é um tipo específico de código de autenticação de mensagem envolvendo uma função hash criptográfica e uma chave...
Más resultados...
Tamaño del texto:
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save