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 semelhante . Também definido como derivar novas classes (subclasses) de classes existentes, como superclasse ou classe base , e depois formá-las em uma hierarquia de classes. Na maioria das linguagens orientadas a objetos baseadas em classes como C++ , um objeto criado através de herança, um "objeto filho", adquire todas as propriedades e comportamentos do "objeto pai", com exceção de: construtores , destruidores, operadores sobrecarregados e amigos funções da classe base. A herança permite aos programadores criar classes que são construídas sobre classes existentes, [1] especificar uma nova implementação enquanto mantêm os mesmos comportamentos ( realizando uma interface ), reutilizar código e estender de forma independente o software original por meio de classes e interfaces públicas . Os relacionamentos de objetos ou classes através 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 livremente tanto para programação baseada em classes quanto para programação baseada em protótipos, mas em uso restrito o termo é reservado para programação baseada em classes (uma classe herda de outra), com a técnica correspondente em programação baseada em protótipos sendo em vez disso, é chamada de delegação (um objeto delega para outro). Os padrões de herança de modificação de classe podem ser predefinidos de acordo com parâmetros simples da interface de rede, de modo que a compatibilidade entre idiomas seja preservada. [2] [3]

Herança não deve ser confundida com subtipagem . [4] [5] Em algumas línguas, a herança e os subtipos concordam, [a] enquanto em outras diferem; em geral, a subtipagem estabelece um relacionamento é-um , enquanto a herança apenas reutiliza a implementação e estabelece um relacionamento sintático, não necessariamente um relacionamento semântico (a herança não garante a subtipagem comportamental). Para distinguir esses conceitos, a subtipagem às vezes é chamada de herança de interface (sem reconhecer que a especialização das variáveis ​​de tipo também induz uma relação de subtipagem), enquanto a herança conforme definida aqui é conhecida como herança de implementação ou herança de código . [6] Ainda assim, a herança é um mecanismo comumente usado para estabelecer relacionamentos de subtipos. [7]

A herança é contrastada com a 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 . A composição implementa um relacionamento tem-um , em contraste com o relacionamento é-um de subtipagem.

História

Em 1966, Tony Hoare apresentou algumas observações sobre registros e, em particular, apresentou a ideia de subclasses de registros, tipos de registros com propriedades comuns, mas discriminados por uma tag de variante e com campos privados para a variante. [8] Influenciados por isso, em 1967 Ole-Johan Dahl e Kristen Nygaard apresentaram um projeto que permitia especificar objetos que pertenciam a classes diferentes, mas tinham propriedades comuns. As propriedades comuns foram coletadas em uma superclasse, e cada superclasse poderia potencialmente ter uma superclasse. Os valores de uma subclasse eram, portanto, objetos compostos, consistindo de um certo número de partes de prefixo pertencentes a várias superclasses, mais uma parte principal pertencente à subclasse. Essas partes foram todas concatenadas. [9] Os atributos de um objeto composto seriam acessíveis por notação de ponto. Esta 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

Herança única
Herança múltipla

Existem vários tipos de herança, baseados em paradigma e linguagem específica. [11]

Herança única
onde as subclasses herdam os recursos 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.

"Herança múltipla  ... era amplamente considerada muito difícil de implementar de forma eficiente. Por exemplo, em um resumo de C++ em seu livro sobre Objective C , Brad Cox na verdade afirmou que adicionar herança múltipla a C++ era impossível. Assim, herança múltipla parecia mais um desafio. Como já havia considerado a herança múltipla 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]

-Bjarne  Stroustrup
Herança multinível
onde uma subclasse é herdada de outra subclasse. Não é incomum que uma classe seja derivada de outra classe derivada, conforme mostrado na figura "Herança multinível".
Herança multinível
A classe A serve como classe base para a classe derivada B , que por sua vez serve como 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 forma:

// classe de implementação da linguagem C++ A { ... }; // Classe base class B : public A { ... }; // B derivado de A class C : public B { ... }; //C derivado de B          
          
          
Este processo pode ser estendido a qualquer número de níveis.
Herança hierárquica
É aqui que uma classe serve como 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 ocorre quando ocorre uma combinação de dois ou mais dos tipos de herança acima. Um exemplo disso é quando uma classe A possui uma subclasse B que possui duas subclasses, C e D. Isso é 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 superclasse , classes base ou classes pai ). A semântica da herança de classe varia de linguagem para linguagem, mas normalmente a subclasse herda automaticamente as variáveis ​​de instância e funções-membro de suas superclasses.

A forma geral de definir uma classe derivada é: [13]

class SubClass : visibilidade SuperClass { // membros da subclasse };   

    

  • Os dois pontos indicam que a subclasse herda da superclasse. A visibilidade é opcional e, se presente, pode ser privada ou pública . A visibilidade padrão é privada . A visibilidade especifica se os recursos da classe base são derivados de forma privada ou pública .

Algumas linguagens também suportam a herança de outras construções. Por exemplo, em Eiffel , os contratos que definem a especificação de uma classe também são herdados por herdeiros. A superclasse estabelece uma interface comum e uma funcionalidade fundamental, que subclasses especializadas podem herdar, modificar e complementar. 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 referenciado é impossível de prever em tempo de compilação . Uma interface uniforme é usada para invocar as funções-membro de objetos de diversas 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 subclassáveis

Em algumas linguagens, uma classe pode ser declarada como não subclassável adicionando certos modificadores de classe à declaração da classe. Os exemplos incluem a finalpalavra-chave em Java e C++ 11 em diante ou a sealedpalavra-chave em C#. Esses modificadores são adicionados à declaração de classe antes da classpalavra-chave e da declaração do identificador de classe. Essas classes não subclassáveis ​​restringem a reutilização , especialmente quando os desenvolvedores só têm acesso a binários pré-compilados e não ao código-fonte .

Uma classe não subclassável não possui subclasses, portanto, 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 ligação antecipada (também chamada de envio estático ) pode ser usada em vez da ligação tardia (também chamada de envio dinâmico ), que requer uma ou mais pesquisas de tabela de métodos virtuais , dependendo se a herança múltipla ou apenas herança única é suportada na linguagem de programação que está sendo usada.

Métodos não substituíveis

Assim como as classes podem não ser subclassáveis, as declarações de método podem conter modificadores de método que evitam que o método seja substituído (isto é, substituído por uma nova função com o mesmo nome e assinatura de tipo em uma subclasse). Um método privado não pode ser substituído simplesmente porque não é acessível por classes diferentes daquela da qual é uma função-membro (embora isso não seja verdade para C++). Um finalmétodo em Java, um sealedmétodo em C# ou um frozenrecurso em Eiffel não pode ser substituído.

Métodos virtuais

Se um método da superclasse for um método virtual , então as invocações do método da superclasse serão despachadas dinamicamente . Algumas linguagens exigem que o método seja especificamente declarado 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 em linha .

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 pelo C++. [14]

Visibilidade da classe base Visibilidade da classe derivada
Derivação privada Derivação protegida Derivação pública
  • Privado →
  • Protegido →
  • Público →
  • Não herdado
  • Privado
  • Privado
  • Não herdado
  • Protegido
  • Protegido
  • Não herdado
  • Protegido
  • Público

Formulários

A herança é usada para correlacionar duas ou mais classes entre si.

Substituindo

Ilustração da substituição de método

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. Este 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 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 estiver marcado com o modificador virtual, abstrato ou de substituição, 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 substituir 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 todas as funcionalidades 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  =  um 
        eu . b  =  b

    def  transformação ( self ,  x ): 
        raise  NotImplementedError

     entradas def ( self ): 
        faixa de retorno  ( self . a , self . b ) 

    def  computar ( self ): 
        retornar  soma ( self . transformar ( valor )  para  valor  em  self . entradas ())

classe  SquareSumComputer ( SumComputer ): 
    def  transform ( self ,  x ): 
        return  x  *  x

classe  CubeSumComputer ( SumComputer ): 
    def  transform ( self ,  x ): 
        return  x  *  x  *  x

Na maioria dos lugares, 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, a delegação explícita , exige mais esforço de programação, mas evita a questão da 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 determinada interface comum; isto é, eles implementam os mesmos métodos. A classe pai pode ser uma combinação de operações implementadas e operações que serão implementadas nas classes filhas. Freqüentemente, não há mudança de interface entre o supertipo e o subtipo – o filho implementa o comportamento descrito em vez de sua classe pai. [17]

Herança vs subtipagem

A herança é semelhante, mas distinta da subtipagem . [4] A subtipagem permite que um determinado tipo seja substituído por outro tipo ou abstração e estabelece um relacionamento é-a entre o subtipo e alguma abstração existente, implícita ou explicitamente, dependendo do suporte da linguagem. O relacionamento pode ser expresso explicitamente por meio de herança em linguagens que suportam herança como mecanismo de subtipagem. Por exemplo, o código C++ a seguir estabelece um relacionamento de herança explícito entre as classes B e A , onde B é uma subclasse e um subtipo de A e pode ser usado como A sempre que B for especificado (por meio de uma referência, um ponteiro ou o próprio objeto).

classe A { público : void DoSomethingALike () const {} };  
 
     


classe B : público A { público : void DoSomethingBLike () const {} };     
 
     


void UseAnA ( const A & a ) { a . FaçaAlgumaCoisa (); }    
  


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 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 com um relacionamento entre tipos . A herança, mesmo em linguagens de programação que suportam a herança como mecanismo de subtipagem, não implica necessariamente em 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 da substituição de Liskov . [18] (Compare conotação/denotação .) Em algumas linguagens OOP, as noções de reutilização e subtipagem de código 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 da herança no projeto 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 Pessoa chamada Aluno que contém a média de notas da pessoa e as aulas cursadas, e outra subclasse de Pessoa chamada Funcionário que contém o cargo, empregador e salário da pessoa.

Ao definir esta hierarquia de herança já definimos certas restrições, nem todas desejáveis:

Solteiro
Usando herança única, uma subclasse pode herdar apenas uma superclasse. Continuando o exemplo dado acima, um objeto Person pode ser Student ou Employee , mas não ambos. O uso de herança múltipla resolve parcialmente esse problema, pois é possível definir uma classe StudentEmployee que herda de Student e Employee . Porém, na maioria das implementações, ele ainda pode herdar de cada superclasse apenas uma vez e, portanto, não suporta casos em que um aluno tenha dois empregos ou frequente duas instituições. O modelo de herança disponível em Eiffel torna isso possível através do suporte à 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 Aluno se torne um objeto Funcionário enquanto mantém o estado de sua superclasse Pessoa . (Esse tipo de comportamento, entretanto, pode ser alcançado com o padrão decorador .) Alguns criticaram a herança, argumentando que ela prende os desenvolvedores aos seus padrões de design originais. [19]
Visibilidade
Sempre que o código do cliente tem acesso a um objeto, geralmente ele tem acesso a todos os dados da superclasse do objeto. Mesmo que a superclasse não tenha sido declarada pública, o cliente ainda poderá converter o objeto para seu tipo de superclasse. Por exemplo, não há como fornecer a uma função um ponteiro para a média de notas e transcrição de um Aluno 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 qualquer código fora da cadeia de herança os acesse.

O princípio da reutilização composta é uma alternativa à herança. Essa técnica oferece suporte ao polimorfismo e à reutilização de código, separando comportamentos da hierarquia de classes primárias 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 classes, 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.

Problemas 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, desempenhado por , combinando propriedades de herança e composição em um novo conceito. [ carece de fontes ]

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 nas subclasses. O uso de 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]

Alegadamente, o inventor do Java, James Gosling, falou contra a herança de implementação, afirmando que não a incluiria se redesenhasse o Java. [19] Projetos de linguagem que separam herança de subtipagem (herança de interface) apareceram já em 1990; [21] um exemplo moderno disso é a linguagem de programação Go .

Herança complexa, ou herança usada em um design insuficientemente maduro, pode levar ao problema do ioiô . Quando a herança foi usada como 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 à medida que a funcionalidade do sistema crescia. Se uma equipe de desenvolvimento combinasse múltiplas camadas de herança com o princípio de responsabilidade única, isso resultaria em muitas camadas muito finas de código, com muitas camadas consistindo em apenas 1 ou 2 linhas de código real. [ citação necessária ] Muitas camadas tornam a depuração um desafio significativo, pois fica difícil determinar qual camada precisa ser depurada.

Outro problema com a herança é que as subclasses devem ser definidas em código, o que significa que os 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 os usuários do programa definam variações de uma entidade em tempo de execução.

Veja também

Notas

  1. ^ Isso geralmente é verdade apenas em linguagens OO baseadas em classes de tipo estaticamente, como C++ , C# , Java e Scala .

Referências

  1. ^ Johnson, Ralph (26 de agosto de 1991). "Projetando classes reutilizáveis" (PDF) . www.cse.msu.edu .
  2. ^ Madsen, OL (1989). “Classes virtuais: um mecanismo poderoso em programação orientada a objetos”. Anais de conferências sobre sistemas, linguagens e aplicações de programação orientada a objetos - OOPSLA '89 . páginas 397–406. doi :10.1145/74877.74919. ISBN 0897913337. S2CID1104130  .
  3. ^ Davies, turco (2021). Métodos Avançados e Aprendizado Profundo em Visão Computacional . Ciência Elsevier. páginas 179–342.
  4. ^ ab Cozinheiro, William R.; Colina, Walter; 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). págs. 125–135. CiteSeerX 10.1.1.102.8635 . doi :10.1145/96709.96721. ISBN  0-89791-343-4.
  5. ^ Cardelli, Luca (1993). Programação Typeful (relatório técnico). Corporação de Equipamentos Digitais . pág. 32–33. Relatório de Pesquisa SRC 45.
  6. ^ abcMikhajlov , 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. págs. 355–382. doi :10.1007/BFb0054099. ISBN 978-3-540-64737-9. Arquivado do original (PDF) em 13/08/2017 . Recuperado em 28/08/2015 .
  7. ^ Tempero, Ewan; Yang, Hong Yul; Nobre, James (2013). O que os programadores fazem com herança em Java (PDF) . ECOOP 2013 – Programação Orientada a Objetos. Notas 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.
  8. ^ Hoare, CARRO (1966). Tratamento de registros (PDF) (relatório técnico). págs. 15–16.
  9. ^ Dahl, Ole-Johan; Nygaard, Kristen (maio de 1967). Declarações de classe e subclasse (PDF) . Conferência de Trabalho IFIP sobre Linguagens de Simulação. Oslo: Centro Norueguês de Computação.
  10. ^ Dahl, Ole-Johan (2004). "O nascimento da orientação a objetos: as linguagens Simula" (PDF) . Notas de aula em Ciência da Computação. Vol. 2635. pp. doi :10.1007/978-3-540-39993-3_3. ISBN 978-3-540-21366-6. {{cite book}}: |journal=ignorado ( ajuda ) ; Ausente ou vazio |title=( ajuda )
  11. ^ "Herança C++" . www.cs.nmsu.edu .
  12. ^ Stroustrup, Bjarne (1994). O Design e Evolução do C++ . Pearson. pág. 417. ISBN 9780135229477.
  13. ^ Schildt, Herbert (2003). A referência completa C++ . Tata McGraw Hill. pág. 417. ISBN 978-0-07-053246-5.
  14. ^ Balagurusamy, E. (2010). Programação Orientada a Objetos com C++ . Tata McGraw Hill. pág. 213. ISBN 978-0-07-066907-9.
  15. ^ substituir (referência C#)
  16. ^ "GotW # 60: Design de classe segura contra exceções, Parte 2: Herança" . Gotw.ca. _ Recuperado em 15/08/2012 .
  17. ^ Venugopal, KR; Buyya, Rajkumar (2013). Dominando C++ . Tata McGraw Hill Education Private Limited. pág. 609. ISBN 9781259029943.
  18. ^ 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.
  19. ^ abc Holub, Allen (1º de agosto de 2003). “Por que estender é mau” . Recuperado em 10 de março de 2015 .
  20. ^ Seiter, Linda M.; Palsberg, Jens; Lieberherr, Karl J. (1996). “Evolução do comportamento dos objetos usando relações de contexto”. Notas de engenharia de software ACM SIGSOFT . 21 (6): 46. CiteSeerX 10.1.1.36.5053 . doi :10.1145/250707.239108. 
  21. ^ América, Pierre (1991). Projetando uma linguagem de programação orientada a objetos com subtipagem comportamental. Escola/Workshop REX sobre os Fundamentos de Linguagens Orientadas a Objetos. Notas de aula em Ciência da Computação. Vol. 489. pp. doi :10.1007/BFb0019440. ISBN 978-3-540-53931-5.

Leitura adicional

  • Meyer, Bertrand (1997). "24. Usando bem a herança" . Construção de software orientado a objetos (2ª ed.). Salão Prentice. ISBN 0-13-629155-4.
  • Samokhin, Vadim (2017). "Herança de implementação é má". HackerNoon . Médio.
Obtido em "https://en.wikipedia.org/w/index.php?title=Inheritance_(programação_orientada a objetos)&oldid=1192201504"