Herança (programação orientada a objetos)
Na programação orientada a objetos , herança é o mecanismo de basear um objeto ou classe em outro objeto ( herança baseada em protótipo ) ou classe ( herança baseada em classe ), mantendo implementação similar . Também definida como derivar novas classes (subclasses) de classes existentes, como superclasse ou classe base , e então formá-las em uma hierarquia de classes. Na maioria das linguagens orientadas a objetos baseadas em classes, como C++ , um objeto criado por herança, um "objeto filho", adquire todas as propriedades e comportamentos do "objeto pai", com exceção de: construtores , destruidores, operadores sobrecarregados e funções amigas da classe base. A herança permite que os programadores criem classes que são construídas sobre classes existentes, [1] para especificar uma nova implementação enquanto mantém os mesmos comportamentos ( realizando uma interface ), para reutilizar código e para estender independentemente o software original por meio de classes e interfaces públicas . Os relacionamentos de objetos ou classes por meio de herança dão origem a um grafo acíclico direcionado .
Uma classe herdada é chamada de subclasse de sua classe pai ou superclasse. O termo herança é usado vagamente para programação baseada em classe e baseada em protótipo, mas em uso restrito o termo é reservado para programação baseada em classe (uma classe herda de outra), com a técnica correspondente em programação baseada em protótipo sendo chamada de delegação (um objeto delega para outro). Padrões de herança modificadores de classe podem ser predefinidos de acordo com parâmetros simples de interface de rede, de modo que a compatibilidade entre linguagens seja preservada. [2] [3]
Herança não deve ser confundida com subtipagem . [4] [5] Em algumas linguagens, herança e subtipagem concordam, [a] enquanto em outras elas diferem; em geral, a subtipagem estabelece uma relação é-um , enquanto a herança apenas reutiliza a implementação e estabelece uma relação sintática, não necessariamente uma relação semântica (herança não garante subtipagem comportamental). Para distinguir esses conceitos, a subtipagem é algumas vezes referida como herança de interface (sem reconhecer que a especialização de variáveis de tipo também induz uma relação de subtipagem), enquanto herança conforme definida aqui é conhecida como herança de implementação ou herança de código . [6] Ainda assim, herança é um mecanismo comumente usado para estabelecer relações de subtipo. [7]
Herança é contrastada com composição de objetos , onde um objeto contém outro objeto (ou objetos de uma classe contêm objetos de outra classe); veja composição sobre herança . Em contraste com o relacionamento is-a da subtipagem , a composição implementa um relacionamento has-a .
Matematicamente falando, a herança em qualquer sistema de classes induz uma ordem parcial estrita no conjunto de classes naquele sistema.
História
Em 1966, Tony Hoare apresentou algumas observações sobre registros e, em particular, a ideia de subclasses de registros, tipos de registros com propriedades comuns, mas discriminados por uma tag variante e tendo campos privados para a variante. [8] Influenciados por isso, em 1967 Ole-Johan Dahl e Kristen Nygaard apresentaram um design que permitia especificar objetos que pertenciam a classes diferentes, mas tinham propriedades comuns. As propriedades comuns eram coletadas em uma superclasse, e cada superclasse poderia potencialmente ter uma superclasse. Os valores de uma subclasse eram, portanto, objetos compostos, consistindo em um certo número de partes de prefixo pertencentes a várias superclasses, mais uma parte principal pertencente à subclasse. Essas partes eram todas concatenadas. [9] Os atributos de um objeto composto seriam acessíveis pela notação de ponto. Essa ideia foi adotada pela primeira vez na linguagem de programação Simula 67. [10] A ideia então se espalhou para Smalltalk , C++ , Java , Python e muitas outras linguagens.
Tipos


Existem vários tipos de herança, baseados em paradigmas e linguagem específica. [11]
- Herança única
- onde subclasses herdam as características de uma superclasse. Uma classe adquire as propriedades de outra classe.
- Herança múltipla
- onde uma classe pode ter mais de uma superclasse e herdar características de todas as classes pai.
"A herança múltipla ... era amplamente considerada muito difícil de implementar eficientemente. Por exemplo, em um resumo de C++ em seu livro sobre Objective C , Brad Cox realmente afirmou que adicionar herança múltipla a C++ era impossível. Assim, a herança múltipla parecia mais um desafio. Como eu havia considerado a herança múltipla já em 1982 e encontrado uma técnica de implementação simples e eficiente em 1984, não pude resistir ao desafio. Suspeito que este seja o único caso em que a moda afetou a sequência de eventos." [12]
- Herança multinível
- onde uma subclasse é herdada de outra subclasse. Não é incomum que uma classe seja derivada de outra classe derivada, como mostrado na figura "Herança multinível".
Herança multinível - A classe A serve como uma classe base para a classe derivada B , que por sua vez serve como uma classe base para a classe derivada C . A classe B é conhecida como classe base intermediária porque fornece um link para a herança entre A e C . A cadeia ABC é conhecida como caminho de herança .
- Uma classe derivada com herança multinível é declarada da seguinte maneira:
// Implementação da linguagem C++ classe A { ... }; // Classe base classe B : public A { ... }; // B derivado de A classe C : public B { ... }; // C derivado de B
- Esse processo pode ser estendido a vários níveis.
- Herança hierárquica
- É aqui que uma classe serve como uma superclasse (classe base) para mais de uma subclasse. Por exemplo, uma classe pai, A, pode ter duas subclasses B e C. A classe pai de B e C é A, mas B e C são duas subclasses separadas.
- Herança híbrida
- Herança híbrida é quando ocorre uma mistura de dois ou mais dos tipos de herança acima. Um exemplo disso é quando uma classe A tem uma subclasse B que tem duas subclasses, C e D. Esta é uma mistura de herança multinível e herança hierárquica.
Subclasses e superclasses
Subclasses , classes derivadas , classes herdeiras ou classes filhas são classes derivadas modulares que herdam uma ou mais entidades de linguagem de uma ou mais outras classes (chamadas superclasses , classes base ou classes pai ). A semântica da herança de classe varia de linguagem para linguagem, mas comumente a subclasse herda automaticamente as variáveis de instância e funções membro de suas superclasses.
A forma geral de definição de uma classe derivada é: [13]
classe SubClasse : visibilidade SuperClasse { // membros da subclasse };
- Os dois pontos indicam que a subclasse herda da superclasse. A visibilidade é opcional e, se presente, pode ser private ou public . A visibilidade padrão é private . Visibility especifica se os recursos da classe base são privately ou public derived .
Algumas linguagens também suportam a herança de outras construções. Por exemplo, em Eiffel , contratos que definem a especificação de uma classe também são herdados por herdeiros. A superclasse estabelece uma interface comum e funcionalidade fundamental, que subclasses especializadas podem herdar, modificar e suplementar. O software herdado por uma subclasse é considerado reutilizado na subclasse. Uma referência a uma instância de uma classe pode, na verdade, estar se referindo a uma de suas subclasses. A classe real do objeto que está sendo referenciado é impossível de prever em tempo de compilação . Uma interface uniforme é usada para invocar as funções de membro de objetos de várias classes diferentes. As subclasses podem substituir funções de superclasse por funções inteiramente novas que devem compartilhar a mesma assinatura de método .
Classes não subclassificáveis
Em algumas linguagens, uma classe pode ser declarada como não subclassificável adicionando certos modificadores de classe à declaração de classe. Exemplos incluem a final
palavra-chave em Java e C++11 em diante ou a sealed
palavra-chave em C#. Tais modificadores são adicionados à declaração de classe antes da class
palavra-chave e da declaração do identificador de classe. Tais classes não subclassificáveis restringem a reutilização , particularmente quando os desenvolvedores têm acesso somente a binários pré-compilados e não ao código-fonte .
Uma classe não subclassificável não tem subclasses, então pode ser facilmente deduzido em tempo de compilação que referências ou ponteiros para objetos dessa classe estão, na verdade, referenciando instâncias dessa classe e não instâncias de subclasses (elas não existem) ou instâncias de superclasses ( upcasting de um tipo de referência viola o sistema de tipos). Como o tipo exato do objeto que está sendo referenciado é conhecido antes da execução, a vinculação antecipada (também chamada de despacho estático ) pode ser usada em vez da vinculação tardia (também chamada de despacho dinâmico ), que requer uma ou mais pesquisas de tabela de métodos virtuais , dependendo se herança múltipla ou apenas herança única são suportadas na linguagem de programação que está sendo usada.
Métodos não substituíveis
Assim como classes podem não ser subclassificáveis, declarações de métodos podem conter modificadores de métodos que impedem que o método seja substituído (ou seja, substituído por uma nova função com o mesmo nome e assinatura de tipo em uma subclasse). Um método privado não é substituível simplesmente porque não é acessível por classes diferentes da classe da qual é uma função membro (isso não é verdade para C++, no entanto). Um final
método em Java, um sealed
método em C# ou um frozen
recurso em Eiffel não podem ser substituídos.
Métodos virtuais
Se um método de superclasse for um método virtual , então as invocações do método de superclasse serão despachadas dinamicamente . Algumas linguagens exigem que o método seja declarado especificamente como virtual (por exemplo, C++) e, em outras, todos os métodos são virtuais (por exemplo, Java). Uma invocação de um método não virtual sempre será despachada estaticamente (ou seja, o endereço da chamada de função é determinado em tempo de compilação). O despacho estático é mais rápido que o despacho dinâmico e permite otimizações como expansão inline .
Visibilidade de membros herdados
A tabela a seguir mostra quais variáveis e funções são herdadas dependendo da visibilidade dada ao derivar a classe, usando a terminologia estabelecida por C++. [14]
Visibilidade da classe base | Visibilidade de classe derivada | ||
---|---|---|---|
Derivação privada | Derivação protegida | Derivação pública | |
|
|
|
|
Aplicações
A herança é usada para correlacionar duas ou mais classes entre si.
Sobreposição

Muitas linguagens de programação orientadas a objetos permitem que uma classe ou objeto substitua a implementação de um aspecto — normalmente um comportamento — que ele herdou. Esse processo é chamado de substituição . A substituição introduz uma complicação: qual versão do comportamento uma instância da classe herdada usa — aquela que faz parte de sua própria classe ou aquela da classe pai (base)? A resposta varia entre as linguagens de programação, e algumas linguagens fornecem a capacidade de indicar que um comportamento específico não deve ser substituído e deve se comportar conforme definido pela classe base. Por exemplo, em C#, o método ou propriedade base só pode ser substituído em uma subclasse se for marcado com o modificador virtual, abstract ou override, enquanto em linguagens de programação como Java, métodos diferentes podem ser chamados para substituir outros métodos. [15] Uma alternativa à substituição é ocultar o código herdado.
Reutilização de código
Herança de implementação é o mecanismo pelo qual uma subclasse reutiliza código em uma classe base. Por padrão, a subclasse retém todas as operações da classe base, mas a subclasse pode sobrescrever algumas ou todas as operações, substituindo a implementação da classe base pela sua própria.
No exemplo Python a seguir, as subclasses SquareSumComputer e CubeSumComputer substituem o método transform() da classe base SumComputer . A classe base compreende operações para calcular a soma dos quadrados entre dois inteiros. A subclasse reutiliza toda a funcionalidade da classe base, com exceção da operação que transforma um número em seu quadrado, substituindo-a por uma operação que transforma um número em seu quadrado e cubo , respectivamente. As subclasses, portanto, calculam a soma dos quadrados/cubos entre dois inteiros.
Abaixo está um exemplo de Python.
classe SumComputer :
def __init__ ( self , a , b ):
self . a = a
self . b = b
def transform ( self , x ):
levantar NotImplementedError
def entradas ( self ):
intervalo de retorno ( self . a , self . b )
def compute ( self ):
retorna soma ( self . transform ( value ) para valor em self . inputs ())
classe SquareSumComputer ( SumComputer ):
def transform ( self , x ):
retornar x * x
classe CubeSumComputer ( SumComputer ):
def transform ( self , x ):
retornar x * x * x
Na maioria dos setores, a herança de classe com o único propósito de reutilização de código caiu em desuso. [ citação necessária ] A principal preocupação é que a herança de implementação não fornece nenhuma garantia de substituibilidade polimórfica — uma instância da classe de reutilização não pode necessariamente ser substituída por uma instância da classe herdada. Uma técnica alternativa, delegação explícita , requer mais esforço de programação, mas evita o problema de substituibilidade. [ citação necessária ] Em C++, a herança privada pode ser usada como uma forma de herança de implementação sem substituibilidade. Enquanto a herança pública representa um relacionamento "é um" e a delegação representa um relacionamento "tem um", a herança privada (e protegida) pode ser pensada como um relacionamento "é implementado em termos de". [16]
Outro uso frequente da herança é garantir que as classes mantenham uma certa interface comum; isto é, elas implementam os mesmos métodos. A classe pai pode ser uma combinação de operações implementadas e operações que devem ser implementadas nas classes filhas. Frequentemente, não há mudança de interface entre o supertipo e o subtipo - a filha implementa o comportamento descrito em vez de sua classe pai. [17]
Herança vs subtipagem
Herança é similar, mas distinta, de subtipificação . [4] A subtipificação permite que um dado tipo seja substituído por outro tipo ou abstração e é dito que estabelece uma relação é- um entre o subtipo e alguma abstração existente, implícita ou explicitamente, dependendo do suporte da linguagem. A relação pode ser expressa explicitamente via herança em linguagens que suportam herança como um mecanismo de subtipificação. Por exemplo, o seguinte código C++ estabelece uma relação de herança explícita entre as classes B e A , onde B é uma subclasse e um subtipo de A e pode ser usado como um A sempre que um B é especificado (via uma referência, um ponteiro ou o próprio objeto).
classe A { público : void DoSomethingALike () const {} };
classe B : público A { público : vazio DoSomethingBLike () const {} };
vazio UseAnA ( const A & a ) { a . DoSomethingALike (); }
void SomeFunc () { B b ; UseAnA ( b ); // b pode ser substituído por um A. }
Em linguagens de programação que não suportam herança como um mecanismo de subtipagem , o relacionamento entre uma classe base e uma classe derivada é apenas um relacionamento entre implementações (um mecanismo para reutilização de código), em comparação a um relacionamento entre tipos . Herança, mesmo em linguagens de programação que suportam herança como um mecanismo de subtipagem, não implica necessariamente subtipagem comportamental . É inteiramente possível derivar uma classe cujo objeto se comportará incorretamente quando usado em um contexto onde a classe pai é esperada; veja o princípio de substituição de Liskov . [18] (Compare conotação/denotação .) Em algumas linguagens OOP, as noções de reutilização de código e subtipagem coincidem porque a única maneira de declarar um subtipo é definir uma nova classe que herda a implementação de outra.
Restrições de projeto
O uso extensivo de herança no design de um programa impõe certas restrições.
Por exemplo, considere uma classe Person que contém o nome, data de nascimento, endereço e número de telefone de uma pessoa. Podemos definir uma subclasse de Person chamada Student que contém a média de notas da pessoa e as aulas cursadas, e outra subclasse de Person chamada Employee que contém o cargo, empregador e salário da pessoa.
Ao definir essa hierarquia de herança, já definimos certas restrições, nem todas desejáveis:
- Solteirice
- Usando herança única, uma subclasse pode herdar de apenas uma superclasse. Continuando o exemplo dado acima, um objeto Person pode ser um Student ou um Employee , mas não ambos. Usar herança múltipla resolve parcialmente esse problema, pois é possível definir uma classe StudentEmployee que herda de Student e Employee . No entanto, na maioria das implementações, ela ainda pode herdar de cada superclasse apenas uma vez e, portanto, não suporta casos em que um aluno tem dois empregos ou frequenta duas instituições. O modelo de herança disponível em Eiffel torna isso possível por meio do suporte para herança repetida .
- Estático
- A hierarquia de herança de um objeto é fixada na instanciação quando o tipo do objeto é selecionado e não muda com o tempo. Por exemplo, o gráfico de herança não permite que um objeto Student se torne um objeto Employee enquanto retém o estado de sua superclasse Person . (Esse tipo de comportamento, no entanto, pode ser alcançado com o padrão decorator .) Alguns criticaram a herança, alegando que ela bloqueia os desenvolvedores em seus padrões de design originais. [19]
- Visibilidade
- Sempre que o código do cliente tem acesso a um objeto, ele geralmente tem acesso a todos os dados da superclasse do objeto. Mesmo que a superclasse não tenha sido declarada pública, o cliente ainda pode converter o objeto para seu tipo de superclasse. Por exemplo, não há como dar a uma função um ponteiro para a média de notas e histórico escolar de um Student sem também dar a essa função acesso a todos os dados pessoais armazenados na superclasse Person do aluno . Muitas linguagens modernas, incluindo C++ e Java, fornecem um modificador de acesso "protegido" que permite que subclasses acessem os dados, sem permitir que nenhum código fora da cadeia de herança os acesse.
O princípio de reutilização composta é uma alternativa à herança. Essa técnica suporta polimorfismo e reutilização de código separando comportamentos da hierarquia de classe primária e incluindo classes de comportamento específicas conforme necessário em qualquer classe de domínio de negócios. Essa abordagem evita a natureza estática de uma hierarquia de classe permitindo modificações de comportamento em tempo de execução e permite que uma classe implemente comportamentos no estilo buffet, em vez de ficar restrita aos comportamentos de suas classes ancestrais.
Questões e alternativas
A herança de implementação é controversa entre programadores e teóricos da programação orientada a objetos desde pelo menos a década de 1990. Entre eles estão os autores de Design Patterns , que defendem a herança de interface e favorecem a composição em vez da herança. Por exemplo, o padrão decorador (como mencionado acima) foi proposto para superar a natureza estática da herança entre classes. Como uma solução mais fundamental para o mesmo problema, a programação orientada a papéis introduz um relacionamento distinto, played-by , combinando propriedades de herança e composição em um novo conceito. [ citação necessária ]
De acordo com Allen Holub , o principal problema com a herança de implementação é que ela introduz acoplamento desnecessário na forma do "problema da classe base frágil" : [6] modificações na implementação da classe base podem causar mudanças comportamentais inadvertidas em subclasses. Usar interfaces evita esse problema porque nenhuma implementação é compartilhada, apenas a API. [19] Outra maneira de afirmar isso é que "a herança quebra o encapsulamento ". [20] O problema surge claramente em sistemas abertos orientados a objetos, como frameworks , onde se espera que o código do cliente herde de classes fornecidas pelo sistema e então substitua as classes do sistema em seus algoritmos. [6]
Segundo consta, o inventor do Java, James Gosling , se manifestou contra a herança de implementação, afirmando que não a incluiria se fosse redesenhar o Java. [19] Projetos de linguagem que desvinculam a herança da subtipagem (herança de interface) surgiram já em 1990; [21] um exemplo moderno disso é a linguagem de programação Go .
Herança complexa, ou herança usada dentro de um design insuficientemente maduro, pode levar ao problema ioiô . Quando a herança era usada como uma abordagem primária para estruturar programas no final da década de 1990, os desenvolvedores tendiam a dividir o código em mais camadas de herança conforme a funcionalidade do sistema crescia. Se uma equipe de desenvolvimento combinasse várias camadas de herança com o princípio de responsabilidade única, isso resultava em muitas camadas muito finas de código, com muitas camadas consistindo de apenas 1 ou 2 linhas de código real. [ citação necessária ] Muitas camadas tornam a depuração um desafio significativo, pois se torna difícil determinar qual camada precisa ser depurada.
Outro problema com herança é que subclasses devem ser definidas em código, o que significa que usuários do programa não podem adicionar novas subclasses em tempo de execução. Outros padrões de design (como Entity–component–system ) permitem que usuários do programa definam variações de uma entidade em tempo de execução.
Veja também
- Padrão de arquétipo – Padrão de design de software
- Problema círculo-elipse
- Raciocínio derrotável – Raciocínio que é racionalmente convincente, embora não dedutivamente válido
- Interface (computação) – Limite compartilhado entre elementos de um sistema de computação
- Substituição de método – Recurso de linguagem em programação orientada a objetos
- Mixin – Classe em linguagens de programação orientadas a objetos
- Polimorfismo (ciência da computação) – Usando uma interface ou símbolo em relação a vários tipos diferentes
- Protocolo – Abstração de uma classe
- Programação orientada a papéis – Paradigma de programação baseado na compreensão conceitual de objetos
- Traço (programação de computadores) – Conjunto de métodos que estendem a funcionalidade de uma classe
- Herança virtual – Técnica na linguagem C++
Notas
Referências
- ^ Johnson, Ralph (26 de agosto de 1991). "Projetando classes reutilizáveis" (PDF) . www.cse.msu.edu .
- ^ Madsen, OL (1989). "Classes virtuais: Um mecanismo poderoso em programação orientada a objetos". Anais de conferência sobre sistemas, linguagens e aplicações de programação orientada a objetos - OOPSLA '89 . pp. 397–406. doi :10.1145/74877.74919. ISBN 0897913337. S2CID 1104130.
- ^ Davies, Turk (2021). Métodos avançados e aprendizado profundo em visão computacional . Elsevier Science. pp. 179–342.
- ^ ab Cook, William R.; Canning, Peter S. (1990). Herança não é subtipagem . Anais do 17º Simpósio ACM SIGPLAN-SIGACT sobre Princípios de Linguagens de Programação (POPL). pp. 125–135. CiteSeerX 10.1.1.102.8635 . doi :10.1145/96709.96721. ISBN 0-89791-343-4.
- ^ Cardelli, Luca (1993). Typeful Programming (Relatório técnico). Digital Equipment Corporation . p. 32–33. Relatório de Pesquisa SRC 45.
- ^ abc Mikhajlov, Leonid; Sekerinski, Emil (1998). Um estudo do problema da classe base frágil (PDF) . Anais da 12ª Conferência Europeia sobre Programação Orientada a Objetos (ECOOP). Notas de aula em Ciência da Computação. Vol. 1445. Springer. pp. 355–382. doi :10.1007/BFb0054099. ISBN 978-3-540-64737-9. Arquivado do original (PDF) em 2017-08-13 . Recuperado em 2015-08-28 .
- ^ Tempero, Ewan; Yang, Hong Yul; Noble, James (2013). O que os programadores fazem com herança em Java (PDF) . ECOOP 2013–Programação Orientada a Objetos. Anotações de aula em Ciência da Computação. Vol. 7920. Springer. pp. 577–601. doi :10.1007/978-3-642-39038-8_24. ISBN 978-3-642-39038-8.
- ^ Hoare, CAR (1966). Manuseio de registros (PDF) (Relatório técnico). pp. 15–16.
- ^ Dahl, Ole-Johan ; Nygaard, Kristen (maio de 1967). Declarações de classe e subclasse (PDF) . IFIP Working Conference on Simulation Languages. Oslo: Norwegian Computing Center.
- ^ Dahl, Ole-Johan (2004). "O Nascimento da Orientação a Objetos: as Linguagens Simula" (PDF) . Da Orientação a Objetos aos Métodos Formais . Notas de Aula em Ciência da Computação. Vol. 2635. pp. 15–25. doi :10.1007/978-3-540-39993-3_3. ISBN 978-3-540-21366-6.
- ^ "Herança C++". www.cs.nmsu.edu . Arquivado do original em 2023-09-24 . Recuperado em 2018-05-16 .
- ^ Stroustrup, Bjarne (1994). O Design e a Evolução do C++ . Pearson. pág. 417. ISBN 9780135229477.
- ^ Schildt, Herbert (2003). A referência completa C++ . Tata McGraw Hill. p. 417. ISBN 978-0-07-053246-5.
- ^ Balagurusamy, E. (2010). Programação Orientada a Objetos com C++ . Tata McGraw Hill. p. 213. ISBN 978-0-07-066907-9.
- ^ substituir (referência C#)
- ^ "GotW #60: Design de classe de exceção segura, parte 2: herança". Gotw.ca . Recuperado em 2012-08-15 .
- ^ Venugopal, KR; Buyya, Rajkumar (2013). Dominando C++ . Tata McGraw Hill Education Private Limited. pág. 609. ISBN 9781259029943.
- ^ Mitchell, John (2002). "10 "Conceitos em linguagens orientadas a objetos" ". Conceitos em linguagem de programação . Cambridge University Press. p. 287. ISBN 978-0-521-78098-8.
- ^ abc Holub, Allen (1 de agosto de 2003). "Por que extends é mal". Arquivado do original em 24 de fevereiro de 2019. Recuperado em 10 de março de 2015 .
- ^ Seiter, Linda M.; Palsberg, Jens; Lieberherr, Karl J. (1996). "Evolução do comportamento de objetos usando relações de contexto". ACM SIGSOFT Software Engineering Notes . 21 (6): 46. CiteSeerX 10.1.1.36.5053 . doi :10.1145/250707.239108.
- ^ America, Pierre (1991). Projetando uma linguagem de programação orientada a objetos com subtipagem comportamental. REX School/Workshop sobre os fundamentos das linguagens orientadas a objetos. Notas de aula em ciência da computação. Vol. 489. pp. 60–90. doi :10.1007/BFb0019440. ISBN 978-3-540-53931-5.
Leitura adicional
- Meyer, Bertrand (1997). "24. Usando bem a herança" (PDF) . Construção de software orientada a objetos (2ª ed.). Prentice Hall. pp. 809–870. ISBN 978-0136291558.
- Samokhin, Vadim (2017). "Herança de implementação é maligna". HackerNoon . Médio.