[Pesquisar este blog]

segunda-feira, 26 de março de 2018

POO::Plena-23-Polimorfismo

POO-P-22-Herança MúltiplaPOO-A-24-Polimorfismo, upcasting e downcasting
A palavra polimorfismo vem do grego poli morfos e significa muitas formas. É a característica chave da orientação a objetos que torna possível utilizar múltiplas formas de construção de objetos. Na OO o polimorfismo de manifesta na sobrecarga, na herança, na sobreposição e também na generalização.

Na verdade, o polimorfismo é um mecanismo único, semelhante a uma força. Em uma direção ele provê a especialização (de tipos), enquanto no sentido oposto oferece a generalização (de tipos). É por meio do polimorfismo que as linguagens OO proporcionam mecanismos de classificação e também de reuso.

Mecanismos do polimorfismo::sobrecarga

A sobrecarga de métodos (method overload) é uma propriedade da OO que permite a existência de dois ou mais métodos com o mesmo nome em uma mesma classe, desde que possuam assinaturas diferentes. Devemos recordar que a assinatura de um método é a combinação de seu nome com a lista dos tipos de seus parâmetros formais. 

Assim, o método estático pow da classe Math, que toma dois parâmetros do tipo double, tem assinatura pow(double, double); o método getClass() da classe Objeto não toma parâmetros, sendo sua assinatura getClass(void); enquanto o método get da classe ArrayList toma um inteiro como argumento e tem assinatura get(int).

É a assinatura diferente que possibilita a diferenciação do método que deve ser acionado no ponto de sua invocação (i.e., na chamada do método); por isso o tipo de retorno é desconsiderado e não faz parte da assinatura, como ilustra a figura que segue.


Desta maneira, métodos conceitualmente semelhantes podem possuir o mesmo nome, diferenciando-se pela lista requerida de argumentos, que possibilita que cada um realize sua operação de maneiras distintas. Por exemplo, a classe String oferece quatro métodos com nome indexOf:
  • indexOf(char) retorna a posição onde se localiza a primeira ocorrência do caractere fornecido como argumento;
  • indexOf(char, int) retorna a posição da primeira ocorrência do caractere suprido como argumento, a partir da posição indicada;
  • indexOf(String) retorna a posição onde se encontra a primeira ocorrência da substring indicada como argumento;
  • indexOf(String, int) retorna a posição da primeira ocorrência da substring dada como argumento, a partir da posição indicada.

A existência de métodos sobrecarregados proporciona flexibilidade para o uso da classe, pois várias formas das operações estão presentes, ao mesmo tempo que mantém a interface da classe simples, porque conceitualmente os métodos sobrecarregados fornecem uma mesma operação.

Construtores também podem ser sobrecarregados, aumentando as possibilidades de criação de objetos. A classe String possui 11 diferentes construtores, que permitem várias formas de instanciação de cadeias de caracteres.

Mecanismos do polimorfismo::herança

A herança (inheritance) é, talvez, a segunda característica mais importante da OO. É o mecanismo que permite a construção de novos tipos de dados (classes) baseada em outros tipos já existentes, possibilitando a criação de hierarquias de classes, como ilustrado a seguir.

Tipicamente, uma hierarquia de classes é como uma árvore cuja raiz é colocada na parte superior do diagrama. A classe-raiz, que dá origem às demais, é colocada no topo, assim, de modo que, para qualquer classe presente na árvore, seus tipos ascendentes ficam acima, enquanto seus descendentes ficam abaixo.

Na herança, uma classe, tomada como superclasse ou classe-pai, pode dar origem a uma ou mais novas classes denominadas de subclasses ou classes-filha. Esta relação pai-filha, identificada pela resposta afirmativa a questão "é um? | é do tipo?", conecta as classes no diagrama e permite, do ponto de vista de implementação, que:
  • as características ancestrais sejam compartilhadas pelos descendentes;
  • novas características possam ser adicionadas nos descendentes;
  • características presentes nos ascendentes sejam modificadas ou restringidas.

O compartilhamento da implementação da superclasse nas subclasse já é um ganho, pois atributos e operações públicas e protegidas tornam-se acessíveis para todos os descendentes da classe, evitando repetição de código e facilitando a manutenção. Isto é um mecanismo efetivo de simplificação de projeto e de reuso.

Além disso, ao adicionar novos atributos e operações, a subclasse é diferenciada de seu ancestral, o que corresponde a um mecanismo de extensão ou de especialização das classes.

Também é possível modificar o comportamento de operações existentes, utilizando outro mecanismo decorrente do polimorfismo denominado sobreposição, que será comentado um pouco mais a frente.

A adição de novos atributos e novas operações é a forma mais comum de aproveitar o mecanismo da herança. Nesta situação procura-se identificar os elementos comuns a um conjunto de classes, mantendo tais  elementos comuns em uma classe que funcionará como ponto de partida comum para um conjunto de outras classes, tal como na ilustração acima, onde a classe Funcionario concentra aspectos comuns de todos os tipos de funcionário de uma empresa, possibilitando que novas classes, especializadas em funções específicas, como Caixa, Motorista, Segurança ou Gerente, possam ser criadas.

Para auxiliar no tarefa de fatoração das características comuns e também na identificação daquelas que são específicas, a organização de uma tabela de classes/tipos e suas funcionalidades pode ser muito útil. Partindo da narrativa descritiva do problema, as classes que representam as entidades/atores do problema usualmente figuram como substantivos. Outros substantivos, adjetivos e complementos costumam indicar os atributos destas entidades; já os verbos apontam as funcionalidades que serão representadas por métodos das classes associadas.

É claro que o benefício da herança é permitir o compartilhamento da implementação dos aspectos comuns destes funcionários, sem necessidade de repetição deste código. Isto mantém a coesão necessária a cada classe, ao mesmo tempo que promove o reuso, facilita a manutenção do código e ainda permite que, no futuro, novas classes sejam adicionadas à hierarquia construída.

Mecanismos do polimorfismo::sobreposição

A sobreposição (ou substituição) de métodos, também conhecida como method overriding, consiste na implementação de um método na subclasse com a mesma assinatura de outro existente na superclasse.

Isto permite dotar a subclasse de uma implementação distinta daquela existente na superclasse, mas mantendo sua interface, o que facilita seu uso por meio do polimorfismo, como será visto mais à frente.

É necessário frisar que a sobreposição envolve a criação de um novo método, com a mesma assinatura, de uma operação existente nas superclasses daquela onde é codificado. Assim, isto constitui um mecanismo distinto da sobrecarga, que envolve a criação de métodos com mesmo nome, mas assinaturas diferentes, em uma mesma classe.

Em conjunto com a herança, a sobreposição permite que uma subclasse tenha comportamento diferente da superclasse, mantendo sua interface. Esta é uma outra forma de especialização da classe, sem ampliação de suas características.

Além disso, métodos considerados inadequados na subclasse, embora não possam ser ocultos (ou seja, deixarem de ser públicos), suas versões sobrepostas podem lançar exceções impedindo seu uso. Com isso, a sobreposição permite a contração de classes, ou seja, a redução de suas funcionalidades.

Mecanismo da sobrecarga::generalização

Sempre é adequado destacar que o polimorfismo é a característica mais importante da OO, pois representa o mecanismo pelo qual vários tipos de objetos, ou seja, várias formas de implementação, podem ser tratadas como sendo de um tipo comum.

Por isso é importante observar que:
  • Uma referência (de instância) é uma variável cujo tipo é uma classe.
  • Cada referência aponta para UM objeto de cada vez.
  • É possível trocar o valor de uma referência, por outra, para que ela aponte para OUTRO objeto.
  • O valor null indica que a referência não aponta qualquer objeto válido.
  • Várias referências diferentes podem apontar para um mesmo objeto.

No Java, em particular, as referências não são endereços de memória, mas identificadores de objetos definidos pela máquina virtual Java (JVM). Por meio desses identificadores, a JVM verifica quando os objetos estão sendo referenciados (em uso aparente). Se a JVM detecta que um objeto deixa de ser referenciado, isto é, quando não existem referências apontando para um objeto específico, ele é marcado pelo Automatic Garbage Collector para descarte (remoção da memória).

Também sabemos que na plataforma Java, qualquer classe é, direta ou indiretamente, uma subclasse de java.lang.Object, pois mesmo numa declaração de classe simples, implicitamente usamos o mecanismo da herança, tomando como superclasse o tipo Object, que constitui assim a raiz da hierarquia de classes Java. Com o C# ocorre o mesmo, mas com a classe System.Object sendo a raiz da hierarquia de classes.

Sendo assim, considerando qualquer hierarquia de classes, toda classe existente é uma subclasse de Object, de modo que uma variável do tipo Object pode armazenar referências de qualquer tipo de objeto:

Object o;
o = new String("Polimorfismo");
o = new Integer(123456);
o = new ArrayList();
o = new FileReader("arquivoTexto.txt");
o = new JFrame("GUI");
o = new Thread("fluxo de processamento");

O polimorfismo permite que um objeto seja, transparentemente, tratado como qualquer outro tipo ascendente (superclasses da qual é derivado), e também como sendo do seu tipo verdadeiro, isto é, como foi instanciado de fato.

A possibilidade de admitir múltiplos tratamentos para um mesmo objeto é a própria definição de polimorfismo. Mas existe uma condição envolvida nesta forma do polimorfismo: um objeto pode tratado como do seu próprio tipo, como sendo de qualquer um de seus ancestrais, até a raiz da hierarquia, ou seja, como do tipo Object.

Se observarmos novamente a figura da hierarquia de funcionários, podemos concluir que as classes Caixa, Motorista, Segurança e Gerente, derivadas diretamente da classe Funcionario, podem ter seus objetos tratados como sendo do tipo Funcionario, ou mesmo Object.

Isso possibilita a generalização, ou seja, uma operação que faz o contrário da herança, que permite a especialização.

Um objeto cuja classe é derivada de outra é como uma composição de objetos em camadas. A camada mais externa corresponde ao tipo real do objeto (por exemplo Gerente) e nela se encontram as adições providas por esta classe (sua especialização em relação à camada anterior). A camada mais interna é sempre aquela que corresponde ao tipo Object, onde estão presentes seus elementos. As camadas intermediárias são aquelas providas pelos descendentes de Object até a camada mais externa.

A figura que segue ilustra um objeto de uma possível classe Comissionado, a qual é derivada de (uma subclasse de) Funcionario, que por sua vez é derivada de (uma subclasse de) Object. Assim, o objeto possui três camadas, uma provida por cada classe de sua hierarquia, a mais interna correspondente a Object e a mais externa correspondente ao seu tipo real (ao tipo de sua criação -- instanciação).


Conforme o tipo da referência usada para acessar um objeto, determina-se quais camadas serão efetivamente acessadas. Se é utilizada uma referência do tipo real (de instanciação), temos acesso a todas os membros públicos deste tipo e de todos os tipos ancestrais (ascendentes). Na figura acima, a referência do tipo Comissionado pode acessar todos os membros públicos do objeto presentes na camada mais externa (Comissionado), na camada intermediária (Funcionario) e também na camada mais interna (Object).

Mas se a referência é do tipo Funcionario, só é possível acessar os membros desta camada (Funcionario) e das mais internas (no caso apenas Object). Uma referência de tipo Funcionário não consegue acessar nada presente em camadas mais externas do que seu tipo, porque sua classe não prevê sua existência, pois foram adicionadas em especializações (subclasses) posteriores.

Da mesma maneira, uma referência do tipo Object só consegue acessar a porção referente a este tipo presente em objetos de qualquer classe.

Por conta da construção dos objetos em múltiplas camadas e do comportamento dos diferentes tipos de referências em relação às camadas presentes nos objetos, temos duas importantes operações decorrentes do polimorfismo:
  • up type casting (ou apenas upcasting) e
  • down type casting (ou apenas downcasting).
Mas este é o assunto do próximo post!

POO-P-22-Herança MúltiplaPOO-A-24-Polimorfismo, upcasting e downcasting

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.
[12] GAMMA, E.; HELM, R.; JOHNSON, R.; VLISSIDES, J.. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.

quinta-feira, 22 de março de 2018

POO::Plena-22-Herança múltipla

POO-P-21-Sobreposição POO-P-23-Polimorfismo
A herança (inheritance) é uma das formas mais importantes do polimorfismo que possibilita a especialização de classes. De fato, é uma técnica que possibilita compartilhar partes da implementação de uma classe com outra, além de permitir a adição de novos elementos, o que torna as novas classes em versões especializadas.

Com isto podem ser criadas famílias de classes, cujos participantes possuem características semelhantes, o que facilita seu desenvolvimento, sua manutenção e também seu reuso. Este mecanismo é único, pois só existe na orientação a objetos.

Considerando que existem muitos tipos de objetos e de entidades, reais e abstratas, estes podem ser classificados em hierarquias, nas quais os elementos superiores agrupam as características comuns dos seus derivados. Esta técnica é usada frequentemente nos diversos campos da conhecimento para criar classificações (ou taxonomias) de seres, componentes, máquinas ou funções.

Desta maneira, a herança é um mecanismo da orientação a objetos que permite representar as relações de semelhança, identificadas afirmativamente pela questão 'é um(a)?', existentes entre conceitos. Uma de suas contribuições diretas é evitar que atributos e operações comuns sejam definidos repetidamente em classes relacionadas. 

Existem duas formas de herança: a herança simples (single inheritance), onde uma única classe é tomada como base na criação de uma subclasse; e a herança múltipla (multiple inheritance), na qual duas ou mais classes são, simultaneamente, tomadas como base para definição de novas classes derivadas.

A linguagem de programação Java foi originalmente projetada para oferecer apenas a herança simples, assim como o C#, apesar de um pouco mais recente.


Herança múltipla

Em algumas situações, as relações existentes entre conceitos não podem ser representadas pela herança simples, que relaciona diretamente uma única superclasse (ou classe base) com sua subclasse (ou classe derivada). Nestes casos, torna-se necessário representar as subclasses relacionadas a partir de duas ou mais outras classes. Esta é a herança múltipla, que permite que uma nova classe compartilhe elementos de duas ou mais superclasses.

Como as linguagens de programação Java e C# não possuem herança múltipla, os exemplos desta seção utilizarão a sintaxe da linguagem C++.

A sintaxe para a construção de classes com herança múltipla em C# é a seguinte:

[acesso] class <NomeSubClasse> : [acesso] <NomeSuperClasse1>,
                                 [acesso] <NomeSuperClasse2>, 
                                 ... ,
                                 [acesso] <NomeSuperClasseN> {
   // corpo da subclasse 
};
// implementação de membros da subclasse

Pode ser observado que, após o nome da subclasse o sinal de dois pontos (':'), segue uma relação dos nomes das superclasses tomadas para herança, separados por vírgulas (',').

A herança múltipla possibilita que uma nova classe seja criada pela combinação de características de várias classes existentes, evitando a repetição destas características no código e permitindo tratamento polimórfico (como será visto no post sobre polimorfismo).

A classe Funcionario, que segue representa um funcionário qualquer de uma empresa que pode ser distinguido dos demais por meio do atributo privado long id que tem papel de um identificador.

class Funcionario {
// membros privados
    long id;
// membros publicos
  public:
    Funcionario(long nid) {
      cout << "Construtor Funcionario: " << nid << endl;
      id = nid;
    }
    ~Funcionario() { cout << "Destrutor Funcionario\n"; }
    long getId() { return id; }
};

Observe que o único construtor disponível em Funcionario exige um parâmetro de tipo long que indica o id do funcionário.

Já a classe Temporario representa os colaboradores contratados por período determinado, sendo seu principal atributo o privado de tipo int periodo.

class Temporario {
// membros privados
    int periodo;
// membros publicos
  public:
    Temporario(int p) {
      cout << "Construtor Temporario: " << p << endl;
      periodo = p;
    }
    ~Temporario() { cout << "Destrutor Temporario\n"; }
    int getPeriodo() { return periodo; }
};

Aqui também temos que o único construtor disponível em Temporario exige um parâmetro de tipo int que requer o número de períodos do contrato de trabalho temporário.

Uma empresa pode possuir engenheiros, que são um tipo particular de funcionário, ou seja, cuja classe Engenheiro pode ser uma subclasse de Funcionario, empregando herança simples. Observe a implementação da classe Engenheiro que segue.

class Engenheiro: public Funcionario {
// membros publicos
  public:
    Engenheiro(long nid): Funcionario(nid) {
      cout << "Construtor Engenheiro: " << nid << endl;
    }
    ~Engenheiro() { cout << "Destrutor Engenheiro\n"; }
};

Note que a classe Engenheiro não possui campos privados. Além disso, o construtor declarado aciona o construtor da superclasse Funcionario para prover o valor requerido do id.

Como a classe Engenheiro toma, por meio da herança simples e pública, a superclasse Funcionario, compartilha seus membros públicos, isto é, a operação getId().

Uma empresa também pode possuir estagiários, que são funcionários temporários, de modo que uma classe Estagiario, como segue, deve utilizar da herança múltipla para compartilhar aspectos das classes Funcionario (o id do funcionário) e também Temporario (o número de períodos de contrato de trabalho).

class Estagiario: public Funcionario, public Temporario {
  public:
    Estagiario(long nid, int p): Funcionario(nid), Temporario(p) {
      cout << "Construtor Estagiario: " << nid << "," << p << endl;
    }
    ~Estagiario() { cout << "Destrutor Estagiario\n"; }
};

Outra vez é possível observar que o construtor declarado aciona os construtores das superclasses Funcionario (para prover o valor requerido do id) e Temporario (para suprir o valor dos períodos).

A classe Estagiario toma, por meio da herança múltipla e pública, as superclasses Funcionario e Temporario; compartilhando seus membros públicos, isto é, as operações getId() (de Funcionario) e getPeriodo() (de Temporario).

A figura que segue ilustra um diagrama de classe UML simplificado que mostra as relações de herança existentes entre as classes Funcionario, Temporario, Engenheiro e Estagiario.


A herança múltipla é um recurso sofisticado que deve ser utilizado com bastante cautela principalmente por programadores menos experientes. Alternativas tais como a herança simples, a agregação ou composição (que foram tratadas em posts anteriores) não devem ser descartadas e podem ser mais simples de implementar e utilizar.

A herança múltipla é um aspecto complexo de C++, muito debatido. Para alguns é uma característica desnecessária, que pode ser substituída por outros mecanismos mais simples, evitando os diversos problemas de ambigüidade decorrentes. Para outros trata questões específicas e quando bem empregada conduz a resultados satisfatórios.

Por conta deste debate, tanto Java como C# não oferecem a herança múltipla, tendo seus projetistas optado por oferecer apenas a herança simples, complementada pelo mecanismo de implementação de interfaces, que será discutido num post mais a frente.

POO-P-21-Sobreposição POO-P-23-Polimorfismo

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.
[12] GAMMA, E.; HELM, R.; JOHNSON, R.; VLISSIDES, J.. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.

segunda-feira, 19 de março de 2018

POO::Plena-21-Sobreposição

POO-P-20-Herança Simples POO-P-22-Herança Múltipla
A herança (inheritance) é um mecanismo da orientação a objetos que permite estabelecer relações de hierarquia entre classes e possibilita compartilhar membros selecionados entre elas. Esta é uma das características únicas e importantes da orientação a objetos [1][2][4][5][6][7].

A classe tomada como ponto de partida na herança torna-se a superclasse (ou classe-base ou classe-pai) de uma família, onde as subclasses (ou classes-derivadas ou classes-filha) criadas a partir desta primeira podem compartilhar de seus membros públicos e protegidos.

Além do compartilhamento de elementos existentes na superclasse, as subclasses podem adicionar novos atributos e operações próprios, de maneira a se tornarem extensões ou especializações de suas superclasses [4][5][6][7].

Mas também é possível substituir membros existentes, restringindo ou modificando as características herdadas. A substituição ou reescrita de métodos é o que se denomina sobreposição ou overriding.


A sobreposição de métodos

A substituição de métodos ou sobreposição de métodos é uma técnica conhecida também como method overriding ou apenas overriding. Constitui uma alternativa de implementação na qual uma subclasse recebe um método com a mesma assinatura de um método existente na superclasse, substituindo-o [6][7].

Esta técnica é tanto conveniente, como interessante, pois permite que a subclasse contenha uma implementação mais apropriada de um método definido na superclasse, o que mantém sua interface, facilita seu uso por meio do polimorfismo, ao mesmo tempo que torna possível alterar como tal operação é realizada [6][7].

Considere a classe Java que segue, denominada Motor.

public class Motor {
   public static final double potenciaMax = 100;

   public double getPotencia() {
      return potenciaMax;
   }
}

A classe Motor possui apenas um campo constante denominado potenciaMax, e um método de observação getPotencia().

Analise agora a classe MotorControlavel, que é uma subclasse de Motor.

public class MotorControlavel extends Motor {
   private double taxa = 0;

   public double getTaxa() { return taxa; }

   public void setTaxa(double taxa) {
      if (taxa<0 || taxa>1) {
         throw new RuntimeException("Taxa invalida: " + taxa);
      }
      this.taxa = taxa;
   }

   @Override
   public double getPotencia() {
      return taxa * potenciaMax;
   }
}

A classe MotorControlavel acrescenta um campo de tipo double denominado taxa e também os métodos de acesso getTaxa() e setTaxa(double) que permitem obter e ajustar o valor do campo taxa, garantindo que tenha um valor entre 0 e 1 (ou seja, 0 e 100%). O método getPotencia() da subclasse MotorControlavel é reescrito para fornecer uma implementação mais adequada, a qual substitui aquela existente na superclasse Motor, ou seja, efetua a sobreposição de método ou method override.

Objetos do tipo Motor e MotorControlavel podem ser instanciados e ter seus métodos getPotencia() acionados, o que provoca a execução da implementação efetivamente disponível em seu tipo, como segue:

Motor motor = new Motor();
MotorControlavel motorContr = new MotorControlavel();
motorContr.setTaxa(0.5);
// método getPotencia() acionado é da classe Motor
System.out.println("motor: " + motor.getPotencia());
// método getPotencia() acionado é da classe MotorControlavel
System.out.println("motorContr: " + motorContr.getPotencia());

Este pequeno exemplo ilustra como um método pode substituir outro existente em suas superclasses, provendo uma nova implementação que substitui o método para a classe onde ocorre a substituição e suas descendentes. Ao mesmo tempo, as várias classes da hierarquia oferecem a mesma interface, o que possibilita o uso polimórfico dos objetos destas classes, mas sempre acionando o método que corresponde ao tipo de origem (de instanciação) do objeto.

Polimorfismo na sobreposição

Considerando a classe Motor e sua subclasse MotorControlavel, sabemos que é possível referenciar um objeto do tipo MotorControlavel por meio de uma referência do tipo Motor, pois um MotorControlavel "é um" Motor:

Motor mc = new MotorControlavel();

Apesar disso, quando é acionado o método getPotencia() por meio da referência do tipo Motor, a versão efetivamente invocada é aquela que pertence a classe MotorControlavel (do tipo de nascença), pois o objeto referenciado pela variável mc é, de fato, do tipo MotorControlavel, não sendo possível "burlar" a substituição efetuada no código por meio de referências de suas superclasses.

Assim, quando ocorre a sobreposição de um método, as instâncias das subclasses ficam impedidas de acionar o método original da superclasse, pois este é o objetivo deste mecanismo.

No entanto, o uso de super permite que, exclusivamente na implementação da subclasse, seja acessada a versão de um método definida na superclasse. O método getPotencia() de MotorControlavel poderia ser sido escrito como segue, aproveitando o código do método sobreposto.

public class MotorControlavel extends Motor {
   private double taxa = 0;

   // demais membros não modificados foram omitidos
   :

   public final double getPotencia() {
      return taxa * super.getPotencia();
   }
}

Embora não seja obrigatório, essa é uma prática habitual, visto que o método reescrito geralmente adiciona funcionalidade à sua versão sobreposta. Não há qualquer problema em não utilizar o método sobreposto quando não for conveniente.

O modificador final

Nas situações onde se deseja impedir que um método seja sobrecarregado, pode ser empregado o modificador final, que indica que o método afetado não poderá ser substituído em qualquer subclasse.

Por exemplo, a classe MotorControlavel poderia sinalizar que o método getPotencia() não deve ser sobreposto em possíveis subclasses como segue:

public class MotorControlavel extends Motor {
   private double taxa = 0;

   // demais membros não modificados foram omitidos
   :

   @Override
   public final double getPotencia() {
      return taxa * potenciaMax;
   }
}

A anotação @Override

Existe uma anotação específica no Java, @Override, a qual indica que o método anotado (isto é, o método onde se aplica) sobrepõe outro existente em uma de suas superclasses. Quando a sobreposição é feita corretamente, esta anotação não produz qualquer efeito, servindo apenas como um lembrete no programa fonte. Mas caso o método anotado não substitua alguma operação, isto é, quando não ocorre a sobreposição, o compilador gera um erro indicando o problema.

Para aplicar a anotação, basta posicioná-la imediatamente antes da declaração do método anotado, com uma das formas que seguem:

@Override public int metodoAnotado() {
   // código do método
}

@Override
public int metodoAnotado() {
   // código do método
}

Embora seja opcional, seu emprego constitui uma boa prática de programação, como feito na class MotorControlavel, pois funciona como um comentário para o programador e garante que sobreposição pretendida, de fato, ocorra.

Regras para sobreposição de métodos

O uso da sobreposição de métodos (method overriding) pode ser vantajoso em muitas situações, mas existem limitações (ou regras) que devem ser observadas em sua aplicação.
  • Um método, em Java ou C#, só pode ser substituído numa subclasse, nunca na própria classe onde foi originalmente declarado.
  • A lista de argumentos deve ser exatamente a mesma (quantidade, tipo e sequência dos parâmetros) do método sobreposto, caso contrário teremos uma mera sobrecarga de método.
  • O tipo de retorno, embora não faça parte da assinatura, deve ser o mesmo daquele declarado no método original, sendo possível indicar uma subclasse do tipo de retorno.
  • Construtores não são herdados, portanto não podem ser sobrepostos.
  • Métodos declarados como final não podem ser sobrepostos.
  • O nível de acesso (visibilidade) não pode ser mais restritivo que o método sobreposto, ou seja, a visibilidade deve ser a mesma ou maior. A maior implicação disso é que métodos públicos não podem ser sobrepostos por versões protegidas ou privadas.
  • Métodos estáticos, de fato, não são sobrepostos, mas podem ser redeclarados.
  • Métodos privados, não são herdados e, assim, não são sobrepostos, mas podem ser redeclarados.
  • Os métodos sobrepostos podem lançar qualquer exceção não-monitoradas (unchecked exceptions), a despeito daquelas lançadas pelo método substituído.
  • Os métodos sobrepostos não podem lançar outras exceções monitoradas (checked exceptions), além daquelas lançadas pelo método substituído ou de suas subclasses. É possível que o método sobreposto lance menos exceções.
A sobreposição de métodos é um mecanismo útil que permite fornecer uma nova implementação para um método existente. Isto possibilita tanto modificar (e especializar) a realização de uma operação em uma subclasse, quanto suprir uma primeira implementação no caso de métodos abstratos, assunto dos próximos posts.

POO-P-20-Herança Simples POO-P-22-Herança Múltipla

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.
[12] GAMMA, E.; HELM, R.; JOHNSON, R.; VLISSIDES, J.. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.

quinta-feira, 15 de março de 2018

POO::Plena-20-Herança simples

POO-P-19-Especificadores e Modificadores POO-P-21-Sobreposição
Os objetos e entidades reais possuem relações entre si que, quando reconhecidas, facilitam o entendimento do conjunto de objetos envolvidos e, ao mesmo tempo, de suas partes. É bastante comum classificar as coisas, criando hierarquias de elementos, onde os grupos superiores contém características comuns, depois separadas em subgrupos, nos quais se incluem características mais específicas, possibilitando definir muitos níveis e grupos. A herança é o mecanismo da orientação a objetos que permite estabelecer este tipo de relação, possibilitando o compartilhamento de características comuns, criando famílias de classes.

A Biologia, por exemplo, organiza os seres vivos numa árvore, agrupando suas características comuns, separando-os em filos, ordens, gêneros e espécies a medida em que são diferenciados. Enquanto os mamíferos contém características comuns encontradas em todos os animais deste tipo, a ordem dos canídeos acrescenta características encontradas apenas nos cães e lobos; assim como a ordem dos felinos inclui características presentes em gatos e tigres. Nesta situação, podemos dizer que os canídeos e os felinos herdam características dos mamíferos. Da mesma forma, uma espécie particular de cão, herda as características dos canídeos, que por sua vez incluem as dos mamíferos. Assim, vemos que as características dos grupos superiores são propagadas, por meio da herança, para os grupos descendentes.

Herança

A herança (inheritance) é um dos mecanismos mais importantes da orientação a objeto s [1][2][4][5][6][7]. De fato, é uma técnica que possibilita a uma classe utilizar atributos e operações definidas em outra classe. Neste sentido a herança representa o compartilhamento de atributos e de operações entre classes, sendo uma característica que só existe na orientação a objetos.

Ao realizar tal compartilhamento, cria-se uma relação hierárquica, do tipo pai e filha, ou seja, a classe pai, tomada como ponto de partida, contém definições que, a critério do programador, poderão ser utilizadas nas classes definidas como filhas. Sob certos aspectos, é como se as características comuns de diversas classes fossem fatoradas em um tipo tomado como classe pai.

É comum que a classe pai seja chamada de classe base (base class) ou superclasse (superclass), enquanto a classe filha é denominada de classe derivada (derived class) ou subclasse (subclass). Nas linguagens de programação Java e C# é mais comum o uso dos termos superclasse e subclasse.

A figura que segue ilustra, por meio de um diagrama de classes UML, a relação existente entre uma superclasse e uma subclasse. Nele se observam as duas classes relacionadas, representadas por retângulos, unidas por uma reta finalizada por um triângulo não preenchido, que aponta para a classe de maior nível hierárquico, ou seja, a classe base ou superclasse. Uma superclasse pode dar origem a várias subclasses, sendo desnecessário dizer que tais subclasses pode ser empregada como novas superclasses, permitindo criar hierarquias com múltiplos níveis.

Além de compartilhar elementos existentes na superclasse, as subclasses podem adicionar novos atributos e operações próprios, ou seja, cada subclasse se torna particular e mais especial do que a superclasse. Assim as subclasses podem ser entendidas como extensões ou especializações de suas superclasses. Como também é possível substituir atributos e operações existentes, as características herdadas podem ser restritas ou modificadas, aumentando as possibilidades da herança. Quando características de uma classe são eliminadas, diz-se que ocorreu uma contração, pois subclasse torna-se mais simples que a superclasse.

No sentido mais amplo e também em sua forma de uso mais comum, a herança é um mecanismo para expressar similaridade entre classes, permitindo representar elementos comuns explicitamente em uma hierarquia de classes [2][4], possibilitando a adição de novos membros na criação de novos tipos derivados e especializados.

A existência de uma relação de herança entre dois tipos pode ser identificada quando uma das perguntas 'é um(a)?' ou 'é do tipo?' é respondida afirmativamente [4][6][7]. Ao considerar as classes Funcionario, Caixa e Cliente, é fácil perceber que Caixa é um (tipo de) Funcionario, mas não o contrário. Assim, Funcionario pode constituir a superclasse de uma hierarquia onde Caixa é uma de suas subclasses. Toda vez que a pergunta 'é um(a)?' determinar o relacionamento entre conceitos, deve ser considerado o emprego da herança na modelagem das classes envolvidas.

Ao mesmo tempo, Cliente não é um (tipo de) Caixa ou Funcionario, portanto constitui uma classe que não faz parte da hierarquia originada em Funcionario, apesar de constituir um dos tipos presentes no cenário considerado.

Quando uma aplicação é construída por meio de um conjunto de classes organizadas hierarquicamente, a ampliação de suas funcionalidades pode, muitas vezes, ser facilitada com a criação de novas classes baseadas nas classes originais. Assim, a herança constitui um mecanismo onde novos tipos de objetos podem ser rapidamente definidos em termos de outros existentes [12]. Outros tipos de funcionário, tais como Motorista, Segurança ou Gerente, poderiam ser facilmente adicionados atendendo os novos requisitos da aplicação. A figura que segue ilustra esta hierarquia possível.

Também deve ser destacado que este mecanismo permite que o conceito de reusabilidade seja verdadeiramente empregado, pois classes tomadas como base podem ser reutilizadas na definição de outras sem que seu código tenha que ser reproduzido ou modificado [6][7]. O emprego da herança evita a cópia direta de código, o que reduz substancialmente a propagação de erros quando os trechos de programação copiados se mostram inapropriados ou incorretos.

Existem duas formas de herança: a herança simples, onde uma única classe é tomada como base na criação de uma subclasse; e a herança múltipla, na qual duas ou mais classes são, simultaneamente, tomadas como base para definição de novas classes derivadas.

A herança simples é tratada neste post, enquanto a herança múltipla será discutida num próximo.

Herança simples

A herança simples é aquela onde a subclasse (classe filha ou classe derivada) é criada a partir de uma única superclasse (classe pai ou classe base), a qual compartilhará seus atributos públicos e protegidos. Esta forma de herança também pode ser conhecida como extensão simples.

A linguagem de programação Java oferece apenas a herança simples, cuja sintaxe requerida para expressar a criação deste tipo de relação é:

public NomeSubclasse extends NomeSuperclasse {
  // corpo da subclasse
}

A palavra reservada extends indica no nome da classe tomada como superclasse na declaração de uma nova subclasse.

Já C# oferece tanto a herança simples, como a múltipla. A sintaxe envolvida na criação de uma nova classe através do mecanismo da herança simples em C# é :

public class <NomeClasseDerivada> : <NomeSuperclasse> {
  // corpo da subclasse
}

Aqui o símbolo de pontuação ':' permite determinar o nome da classe tomada como superclasse na declaração de uma nova subclasse.

Tanto em Java como em C#, ao usar a sintaxe indicada acima, a subclasse passa a compartilhar todos os membros públicos e protegidos presentes na superclasse. Assim, os atributos e métodos presentes na superclasse podem ser livremente utilizados na programação da subclasse e, também das subclasses desta. Mas as instâncias da superclasse e da subclasse tem acesso apenas aos elementos públicos da superclasse.

Isto significa que um membro declarado público em uma classe sempre será acessível para todas as instâncias desta classe, para todas as subclasses e todas as instâncias de sua subclasses.

Os membros protegidos de uma classe poderão ser sempre utilizados na programação de subclasse e das subclasses das subclasses, mas nunca serão acessíveis para os objetos da classe, nem os objetos de quaisquer subclasses.

Finalmente, os elementos privados presentes na superclasse são completamente inacessíveis, o que evidencia a utilidade do níveis de acesso existentes.

Apesar das subclasses herdarem todos os membros públicos e protegidos de suas superclasses, os construtores, mesmo que públicos, não são herdados pelas subclasses, exigindo que novos construtores sejam supridos quando necessário. Sempre é importante destacar que, naturalmente, cada superclasse pode dar origem a um número qualquer de subclasses e, estas a outras subclasses.

A classe Java que segue, denominada Superclasse, declara três atributos a, b e positive; respectivamente dos tipos int, double e boolean; com acesso public, protected e private.

O atributo público a não possui métodos de acesso, pois não existem restrições para seu uso. Já o atributo protegido b, embora existam restrições para seus valores, quando tem conteúdo positivo, tal informação deve ser indicada pelo atributo privado positive como true, enquanto se b é negativo, positive deve ser false. Para garantir que estes dois atributos estejam corretamente relacionados, b é protegido e seu método de mutação setB(double) garante o valor adequado de positive para qualquer valor de b. O atributo positive, portanto, não pode sofrer ajustes diretos, possuindo apenas um método de acesso apropriado para seu tipo boolean, que é isPositive().

O único construtor existente garante que a inicialização do atributo b seja corretamente refletida pelo atributo positive. O método toString() facilita consultar o estado dos três atributos da classe.

public class Superclasse {
   public int a;
   protected double b;
   private boolean positive;

   public Superclasse() {
      setB(0.0);
   }

   public double getB() { return b; }
   public boolean isPositive() { return positive; }

   public void setB(double b) {
      if (b<0) positive = false;
      else positive = true;
      this.b = b;
   }

   public String toString() {
      return String.format("a = %d\nb = %f\npositive = %s\n",
         a, b, positive);
   }
}

O quadro que segue ilustra se os membros de Superclasse são acessíveis em seu código, por suas instâncias, no código de suas subclasses e por instâncias de suas subclasses. Os membros originais são aqueles declarados na própria classe.


O fragmento Java que segue mostra um possível uso da classe Superclasse.

Superclasse obj = new Superclasse();

System.out.println(obj);
obj.a = 13;
obj.setB(-1.5);
System.out.println(obj);

Sua execução produz a seguinte saída:

obj ==> a = 0
b = 0,000000
positive = true
obj ==> a = 13
b = -1,500000
positive = false

Uma classe Java derivada de Superclasse, denominada Subclasse, poderia ser como segue.

public class Subclasse extends Superclasse {
   public long l;
   protected double d;
   private double rate;

   public Subclasse() {
      super();
      // emprego explícito do construtor da superclasse
   }

   public double getD() { return d; }
   public double getRate() { return rate; }

   public void setD(double d) {
      if (d<0) {
         throw new RuntimeException("Valor inválido: " + d);
      }
      this.d = d;
      rate = b / d;
   }
}

Na subclasse denominada Subclasse, é possível ver que três novos atributos foram acrescentados. O atributo de tipo long l é público, pode ser usado irrestritamente e, por isso, não possui métodos de acesso ou mutação.

O atributo protegido de tipo long chamado d só aceita valores positivos, como garante o seu método de mutação associado setD(double). Além disso, valores válidos de d acarretam no armazenamento da razão b/d, onde b é um campo protegido da superclasse, tal como d é nesta classe. O valor da razão é obtido pelo método de acesso getRate().

O quadro que segue ilustra se os membros de Superclasse são acessíveis em seu código, por suas instâncias, no código de suas subclasses e por instâncias de suas subclasses. Os membros originais são aqueles declarados na própria classe, enquanto os membros herdados são aqueles oriundos de seus ancestrais (suas superclasses).


Também é importante notar que a classe Subclasse possui um construtor sem parâmetros, tal qual o default, que foi adicionado aqui apenas para mostrar o uso explícito de super.


super

A palavra reservada super indica a superclasse da classe onde onde é usado.

No caso de construtores, super só pode ser usado na primeira linha de código do construtor, explicitando o acionamento de um construtor (parametrizado ou não) da superclasse, que deve existir. Caso seja acionado o construtor default (sem parâmetros) da superclasse, a chamada a super() pode ser omitida.

Aqui é necessário destacar que quando a superclasse possui apenas construtores parametrizados, torna-se necessário

Em resumo, membros privados não são compartilhados, membros protegidos são acessíveis apenas no código de subclasses e membros públicos são acessíveis pelo código de subclasses e também por instâncias da classe e sua subclasses. Assim, fica a critério do projetista determinar quais os especificadores de acesso serão empregados em cada um dos membros das classes e dos membros adicionais das subclasses.

Usualmente os membros das classes devem ser privados, exceto nos casos onde não existam restrições para seu conteúdo. Métodos de acesso possibilitam usar membros privados indiretamente, garantindo a validade de seu conteúdo e a consistência com outros membros da classe, até mesmo quando não existem restrições. 

Os membros protegidos são destinados ao controle de aspectos internos da classe, mas que poderiam ser alvo de modificações em subclasses, desde que isto não comprometa a consistência de outros membros da classe. Quando o uso de um membro é muito complexo ou permite tornar o objeto inconsistente, é adequado declará-lo como privado, acrescentando métodos que permitam seu uso indireto e consistente.

A classe Object

Existe uma classe, tanto no Java como em C# que é muito importante. No Java, esta classe é Object, do pacote java.lang, que dá origem a toda hierarquia de classes do Java. No C# temos a mesma situação, a classe Object pertence ao namespace System. Todas as classes, de maneira direta ou indireta, possuem a classe Object como ancestral, ou seja, como superclasse. Isto ocorre porque toda vez que se declara uma nova classe, sem indicação explícita de uma classe base, é tomada implicitamente a classe Object como ancestral direto, o que cria uma raiz única para toda hierarquia de classes do Java ou do C#.

Observe que a declaração de um classe Horario, como abaixo, não indica, explicitamente uma superclasse:

public class Horario {
   private int hora;
   private int minuto;

   public Horario(int hora, int minuto) {
      setHora(hora);
      setMinuto(minuto);
   }

   public int getHora() { return hora; }
   public int getMinuto() { return minuto; }

   public void setHora(int hora) { 
      if (hora<0 || hora>23) {
         throw new RuntimeException("Hora invalida: " + hora);
      }
      this.hora = hora;
   }
   public void setMinuto(int minuto) {
      if (minuto<0 || minuto>59) {
         throw new RuntimeException("Minuto invalido: " + minuto);
      }
      this.minuto = minuto;
  }
}

Mas realmente esta declaração corresponde a:

// Java
public class Horario extends Object {
  :
}

// C#
public class Horario : Object {
  :
}

Assim, todos os objetos Java, incluindo qualquer tipo de array, compartilham os recursos disponíveis na classe Object, cuja API inclui elementos destinados à:
  • semântica de comparação (métodos equals(Object) e hashcode());
  • coleta de lixo (método finalize());
  • reflexão (método getClass());
  • representação textual (método toString()); e
  • sincronização de threads (métodos wait(), notify() e notifyAll()).
Em C# a situação é idêntica. Além disso, temos duas consequências importantes do fato de qualquer classe ser derivada de java.lang.Object ou de System.Object:
  • É a herança da classe Object que define uma infraestrutura comum a todos os objetos, possibilitando que acionem os métodos públicos lá definidos, como toString() e os demais.
  • Como toda classe é derivada de Object, todas as instâncias, quaisquer sejam suas classes específicas, podem ser tratadas como instâncias de Object, justificando porque muitas classes da API Java lidam com o tipo Object, pois isto permite operar com qualquer tipo de objeto.

Modificador final

O modificador final pode ser aplicado a uma declaração de atributo, método ou classe. Nestas três situações ele indica que a declaração onde se aplica é a definitiva, ou seja, que substituições ou modificações não serão aceitas.

Uma classe declarada como final não pode ser tomada como base de outra, ou seja, uma classe final não permite o uso da herança na construção de novas subclasses.

Assim, se a classe Horario fosse declarada como:

public final class Horario extends Object {
  :
}

Seria incorreto fazer:

public class HorarioDetalhado extends Horario {
  :
}

Se tal código for compilado, será produzido o erro:

HorarioDetalhado.java:1: error: cannot inherit from final Horario
public class HorarioDetalhado extends Horario {
                                      ^
1 error

Geralmente classes são feitas finais por motivos de segurança, quando não se deseja que terceiros modifiquem as classes existentes por meio de subclasses, garantindo a integridade do projeto.

Argumentos de um método que não devem ser modificados podem ser declarados como final na própria lista de parâmetros, mas como podem ser copiados e modificados indiretamente, o emprego desta declaração é meramente convencional, para comunicar tal intenção aos usuários do método.

O uso de final em atributos, quando associado ao modificador static, permite definir constantes, como mostrado no post Membros estáticos. Mas isoladamente impede que tal atributo seja modificado em subclasses. Da mesma forma, métodos declarados como final não poderão ser substituídos nas subclasses da classe onde foram declarados.

Estas duas últimas situações serão detalhadas no post sobre sobreposição.

Considerações finais

A herança simples é uma das características mais utilizadas da orientação a objeto na construção de sistemas, pois facilita o projeto ao permitir que classes contendo a infraestrutura de outras tenham seus membros compartilhados, seletivamente conforme determinado pelos seus especificadores de acesso, possibilitando a construção de outras, sem necessidade de copiar código, o que agiliza o desenvolvimento e simplifica as atividades de manutenção.


POO-P-19-Especificadores e Modificadores POO-P-21-Sobreposição

Referências Bibliográficas

[1] JAMSA, K.; KLANDER, L.. Programando em C/C++: a bíblia. São Paulo: Makron Books, 1999.
[2] PAGE_JONES, M.. Fundamentos do Desenho Orientado a Objeto com UML. São Paulo: Makron Books, 2001.
[3] SOMMERVILLE, I.. Software Engineering. 6th. Ed. Harlow: Pearson, 2001.
[4] DEITEL, H.M.; DEITEL, P.J.. Java: como programar. 6a. Ed. São Paulo: Pearson Prentice-Hall, 2005.
[5] SAVITCH, W.. C++ Absoluto. São Paulo: Pearson Addison-Wesley, 2004.
[6] JANDL JR., P. Introdução ao C++. São Paulo: Futura, 2003.
[7] JANDL JR., P.. Java - guia do programador. 3a. ed. São Paulo: Novatec, 2015.
[8] RUMBAUGH, J.; BLAHA, M.; PREMERLANI, W.; EDDY, F.; LORENSEN, W.. Object-oriented modeling and design. Englewoods Cliffs: Prentice-Hall, 1991.
[9] STROUSTRUP, B.. The C++ Programming Language. 3rd Ed. Reading: Addison-Wesley, 1997.
[10] LANGSAM, Y.; AUGENSTEIN, M. J.; TENENBAUM, A. M.. Data structures using C and C++. 2nd Ed. Upper Saddle River: Prentice-Hall, 1996.
[11] WATSON, K.; NAGEL, C.; PEDERSEN, J.H.; REID, J.D.; SKINNER, M.; WHITE, E.. Beginning Microsoft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.
[12] GAMMA, E.; HELM, R.; JOHNSON, R.; VLISSIDES, J.. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.