[Pesquisar este blog]

quinta-feira, 25 de janeiro de 2018

POO::Fundamentos-06-Sobrecarga

POO-F-05-Métodos POO-F-07-Construtores
A sobrecarga de métodos (method overload) é a possibilidade de existirem várias operações com o mesmo nome em uma mesma classe [6][7]. É a manifestação mais simples do polimorfismo, uma das características mais importantes da OO.

Isto significa que dentro de uma classe podem existir métodos homônimos (com o mesmo nome), mas desde que possuam assinaturas (signatures) diferentes. A assinatura de um método, ou sua method signature, é a lista dos tipos de seus parâmetros formais, na exata ordem em que foram declarados, mesmo que existam repetições.

Sendo assim uma operação é considerada sobrecarregada (overloaded) quando existirem várias declarações envolvendo um mesmo nome, mas sempre com assinaturas diferentes (na quantidade ou no tipo de seus parâmetros).

Considere a classe Contador, criada com o propósito de armazenar o valor atual de uma contagem, além de prover vários métodos sobrecarregados de incremento, denominados inc, como no código que segue.

class Contador {
   double contagem;

   Contador() {
      contagem = 0;
   }
   void inc() {
      contagem = contagem + 1;
   }
   void inc(int i) {
      contagem = contagem + i;
   }
   void inc(double d) {
      contagem = contagem + d;
   }
}

Neste exemplo, existem três métodos denominados inc, cujas assinatura são:
  • inc(void)
  • inc(int)
  • inc(double)
Assim, inc é um método sobrecarregado, dotado de três versões que permitem incrementar o valor da contagem (um atributo interno da classe) com, respectivamente, o valor default um (um incremento unitário portanto), um valor inteiro indicado como argumento; ou um valor double dado como argumento. O uso do mesmo nome é apropriado por que os três métodos fazem, conceitualmente, a mesma operação de incrementar o valor da contagem, assim a sobrecarga de métodos nesta situação é adequada porque existem maneiras diferentes de realizar-se tal operação.

Conforme o tipo e valor dos argumentos fornecidos, é selecionado o método adequado na classe, como na figura acima e no fragmento de código que segue.

// cria novo contador iniciado em zero
Contador cont = new Contador();

// efetua incrementos com operações sobrecarregadas
cont.inc(); // incremento unitário default
cont.inc(5); // incremento de cinco unidades inteiras
cont.inc(3.5); // incremento real de 3.5

System.out.println(cont.contagem); // exibe o resultado 9.5

Destacamos que o valor de retorno não faz parte da assinatura, pois a resolução da sobrecarga, isto é, a seleção do método adequado se dá pelo tipo e número dos argumentos fornecidos. A resolução da sobrecarga emprega uma série de critérios que visa fazer a melhor escolha possível considerando os tipos dos argumentos na chamada e as versões disponíveis da função sobrecarregada, mas a ambiguidade não é permitida, exigindo que cada método sobrecarregado tenha uma assinatura distinta (listas distintas de número e tipos dos parâmetros formais).

Outro exemplo do emprego da sobrecarga poderia ser na operação de um sub-array a partir de um array. Dado um array de n posições, um outro inteiro m poderia indicar o tamanho da porção inicial do sub-array (desde que m<=n). Para obter uma porção intermediária do array de n posições, deveriam ser especificados inteiros i e f, onde i<=f<=n. Um sub-array da parte final pode ser obtido com esta mesma operação. Neste caso temos duas versões de obtenção de sub-array:
  • uma com dois parâmetros, o array e o tamanho m inteiro da porção inicial a ser extraída;
  • outra com três parâmetros, o array, a posição inicial i inteira e a posição final f inteira da porção a ser extraída.
E qual o tipo do array? Para cada tipo de array deveriam ser fornecidas as duas operações, evitando que o usuário da operação precise realizar coerção entre tipos! Assim, uma classe SubArray poderia ter este par de métodos para todos os tipos primitivos do Java (byte, short, int, long, float, double, boolean e char) e também para o tipo Object. Em C# a ideia é a mesma.

Uma versão "incompleta" de SubArray, apenas para o tipo int, poderia ser como segue:

class SubArray {
// obtém subarray inicial com tamanho m (posição m não inclusa)
   int[] subArray(int[] a, int m) {
      // verifica se tamanho m é válido
      if (m > a.length) {
         throw new RuntimeException("valor invalido de m.");
      }
      // aloca novo array
      int[] aux = new int[m];
      // copia elementos
      for(int p=0; p<m; p++) aux[p] = a[p];
      // retorna resultado
      return aux;
   }
// obtém subarray entre início (i-incluso) e fim (f- não incl)
   int[] subArray(int[] a, int i, int f) {
      // verifica se posições são válidas
      if (i > f || f > a.length) {
         throw new RuntimeException(
             "valores invalidos de i e/ou f.");
      }
      // aloca novo array
      int[] aux = new int[f-i];
      // copia elementos
      for(int p=i; p<f; p++) aux[p] = a[p];
      // retorna resultado
      return aux;
   }
}

Para usar esta classe basta:

// dispor de um array (de inteiros)
int[] array = { 0, 1, 2, 3, 4, 5, 6 }; // inicialização automática

// instanciar objeto da classe SubArray
SubArray obj = new SubArray();

// obtém subarray inicial {0,1,2}
int[] subArrayInicio = obj.subArray(array, 3);

// obtém subarray intermediário {3,4,5}
int[] subArrayMeio = obj.subArray(array, 3, 6);

// obtém subarray final {3,4,5,6}
int[] subArrayFim = obj.subArray(array, 3, 7);

// obtém (sub)array cópia {0,1,2,3,4,5,6}
int[] arrayCopia = obj.subArray(array, 0, 7);

A classe SubArray poderia ser melhorada, com uso de métodos estáticos e fatoração do próprio código, evitando a repetição de código semelhante, o que será feito na lição sobre membros estáticos.

A sobrecarga também pode ser usada com construtores, como será visto na próxima lição, tornando-a muito conveniente para estabelecer também formas diferentes para a criação de objetos.

Concluindo, a sobrecarga (method overload) é o emprego do mesmo nome para operações sobre parâmetros de diferentes tipos [4][6][7][9], o que representa uma forma do polimorfismo. Seu emprego possibilita a construção de classes versáteis, onde a realização de certas operações admitem várias formas, cada qual conveniente para uma situação da programação, mais apropriada para cada uso.

POO-F-05-Métodos POO-F-07-Construtores

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 Microsft Visual C# 2008. Indianapolis: Wiley Publishing, 2008.

Nenhum comentário: