Desenvolvimento - C#
Sobrecarga, Herança, Polimorfismo e Exceção em C#
Este artigo é o resultado de uma pesquisa do Diego sobre a implementação de alguns dos princípios básicos de Orientação a Objetos fazendo uso de C#, onde nós procuramos descrever conceitualmente alguns destes conceitos, fornecendo exemplos didático dos mesmos, para que você possa ter noção de como implementá-los em C#.
por Everton Coimbra de AraújoEste é o segundo artigo que submeto ao Linha de Código neste semestre. Como dito no primeiro artigo (http://www.linhadecodigo.com.br/Artigo.aspx?id=2605), será publicada uma série de material, resultado obtido através de pesquisas e laboratórios com meus alunos da UTFPR, campus Medianeira. Reforço o empenho e dedicação destes alunos, que foi extremamente comprometido com a pesquisa e aprendizado durante os semestres que mantivemos contato próximo nas disciplinas que a eles eu ministrava. Isso me motivou muito e fazer com que o resultado dos trabalhos deles aparecesse para o mundo e, nada melhor do que o Linha de Código para isso.
|
Neste artigo apresento o Diego Stiehl, que pode ser contactado pelo email diego.stiehl@gmail.com. O Diego foi meu aluno em algumas disciplinas de sua graduação e está concluindo neste segundo semestre de 2009 suas disciplinas agora entra no processo de TCC (Trabalho de Conclusão de Curso). O Diego, apesar de ter trabalhado comigo neste artigo sobre .NET tem uma grande experiência no desenvolvimento de aplicações em Delphi e Java. Desta forma, estejam a vontade em solicitar o curriculum dele. |
Este artigo é o resultado de uma pesquisa do Diego sobre a implementação de alguns dos princípios básicos de Orientação a Objetos fazendo uso de C#, onde nós procuramos descrever conceitualmente alguns destes conceitos, fornecendo exemplos didático dos mesmos, para que você possa ter noção de como implementá-los em C#.
Introdução
O paradigma da orientação a objetos é algo já presente na vida de profissionais relacionados às áreas de desenvolvimento e análise. Muitos conceitos existem, os quais são amplamente trabalhados em inúmeros livros e muitas vezes com enorme propriedade.
O trabalho proposto neste material objetiva abordar sobrecarga, herança, polimorfismo e exceção, porém dando ênfase na implementação destes conceitos fazendo uso da linguagem C#, fazendo, quando possível, uma analogia com Java.
Para o melhor aproveitamento das informações aqui constantes, espera-se que o leitor já possua algum conhecimento de orientação a objetos ou de alguma linguagem de programação baseada na mesma (preferencialmente C#). Mas, caso você não tenha este conhecimento prévio, leia o material e busque relacioná-lo com outros materiais, disponibilizados em livros, por exemplo.
Herança
A herança diz respeito à extensibilidade de classes no modelo orientado a objetos. Quando se diz estender determinada classe, entende-se que uma nova classe será criada, contendo suas próprias propriedades e características e, agregando a esta nova classe as propriedades e características de outra já existente a qual é conhecida também como um classe Genérica (ou superclasse). Já a nova classe é conhecida como classe especializada (ou subclasse). Um exemplo fácil de ser visualizado é o caso de uma classe que represente pessoas, como pode ser visualizado no diagrama de classes da Figura 1.
Figura 1. Diagrama de classes representando Herança |
Na Figura 1, vê-se a classe genérica (Pessoa) e suas duas classes
específicas (Jurídica e Física), representadas através de um diagrama de
classes (oferecido pela UML), o qual possibilita, entender que uma pessoa
possui um nome e possibilidade de escrevê-lo através de um método. Ao tratar-se
de uma pessoa jurídica, é possível perceber que a mesma também é uma pessoa,
possuindo então um nome, que pode ser escrito usando-se o mesmo método, porém ela
contém um número de CNPJ que é exclusivo de pessoas jurídicas e que pode ser
validado através de um método também específico. O mesmo acontece com relação
ao CPF para uma pessoa física. Não está sendo considerado nesta explicação a
visibilidade dos atributos e métodos.
Existe também a possibilidade de realizar uma herança através do uso de interfaces, que forçam as classes a seguirem determinados “contratos”, porém este assunto será abordado na sessão referente ao polimorfismo.
Basicamente, herança é uma técnica utilizada para não precisar “reinventar a roda” ou manter código-fonte duplicado durante a programação de uma aplicação orientada a objetos, ou seja, a idéia do princípio de herança (por extensão, ou comportamento) é a reutilização de códigos.
Herança com C#
C# é uma liguagem totalmente orientada a objetos, desta forma, ela permite a implementação de todos os princípios de orientação a objetos, como a Herança, explicada anteriormente.
A implementação da herança em C# se dá através do operador dois-pontos (:), que deve ser utilizado após o nome da classe especializada que está sendo criada. Para exemplificar, observe na Listagem 1 a declaração das classes exemplificadas na Figura 1.
class Pessoa class Fisica : Pessoa class Juridica : Pessoa |
Listagem 1. Declaração de classes e
herança em C# |
A Listagem 2 mostra a implementação completa do mesmo exemplo da Figura 1.
//Pessoa namespace Heranca { public class Pessoa { public string Nome { get; set; }
public void escreverNome() { Console.Write(this.Nome); } } }
//Pessoa Física namespace Heranca { public class Fisica : Pessoa { public string Cpf { get; set; }
public Boolean validarCpf() { return this.Cpf.Length == 11; } } } //Pessoa Jurídica namespace Heranca { public class Juridica : Pessoa { public string Cnpj { get; set; }
public Boolean validarCnpj() { return this.Cnpj.Length == 14; } } } |
Listagem 2. Herança utilizando extensão em C# |
Polimorfismo
A segunda característica e funcionalidade oferecida pela OO que apresentamos neste material é o polimorfismo, o qual significa “muitas formas”. O Polimorfismo permite, em uma de suas metodologias de aplicação, que diferentes classes tenham métodos com a mesma assinatura (mesmo contrato), porém estes métodos (em suas respectivas classes) podem possuir comportamentos diferentes, de acordo à necessidade de cada classe que o implementa.
A implementação do polimorfismo pode ser realizada fazendo uso de interfaces, ou classes abstratas, onde ocorrem apenas a implementação das assinaturas dos métodos, ou seja, do contrato. Desta forma o comportamento deve ser implementado nas classes concretas que implementam as interfaces ou estendem as classes abstratas.
Dando sequência ao exemplo da Figura 1, aplicando agora o polimorfismo, tem-se o diagrama de classes apresentado na Figura 2.
Figura 2.
Diagrama de classes representando Polimorfismo |
Na Figura 2, nota-se que as classes Física e Jurídica, que estendem da classe Pessoa, não possuem métodos para a validação do documento pertencente a cada uma delas, porém estas classes fazem acesso ao método validarDocumento() na classe Pessoa, a qual foi obrigada a implementá-lo, ou torná-lo abstrato, pelo fato da mesma ter feito o uso da interface Validador.
A interface não implementa seu método, mas diz que toda classe que fizer a utilização da mesma terá a obrigação de implementá-lo, por isto esta ligação é tida como um contrato. Como a classe genérica faz o uso da interface para validar e as outras duas classes estendem diretamente dela, elas acabam tendo um comportamento comum (o fato de ter um documento que pode ser validado).
Polimorfismo com C#
Em C#, diferentemente de outras linguagens, como Java, para implementar uma interface em alguma classe usa-se a mesma técnica que a usada para estender, usando-se dois pontos e o nome da interface (caso haja necessidade de mais de uma interface, a vírgula deve ser usada).
Observe o exemplo na Listagem 3.
//Interface para validar o documento namespace Polimorfismo { interface Validador { Boolean validarDocumento(); } }
//Pessoa namespace Polimorfismo { abstract class Pessoa: Validador { public string Nome { get; set; }
public void escreverNome() { Console.Write(this.Nome); }
public virtual bool validarDocumento() { throw new NotImplementedException(); }
} }
//Pessoa Física namespace Polimorfismo { class Fisica : Pessoa { public string Cpf { get; set; }
public override bool validarDocumento() { return this.Cpf.Length == 11; } } }
//Pessoa Jurídica
namespace Polimorfismo { class Juridica : Pessoa { public string Cnpj { get; set; }
public override bool validarDocumento() { return this.Cnpj.Length == 14; } } }
|
Listagem 3. Utilização de interface em C# |
Nota-se que não houve a necessidade da criação de um método específico para o tratamento
dos dados do documento em cada classe, pois a interface trata esta questão,
porém nota-se que quem implementou o método da interface foi a classe Pessoa,
sendo que ele foi transformado em virtual (não possui corpo definido), obrigando
as classes específicas a sobrescrever este método (em seguida este assunto será
apresentado). Sabendo-se que uma classe implementa a interface Validador se supõe
que a mesma possui um documento a ser validado, sendo assim o método da
interface deve ser implementado de acordo com a realidade da classe que o
implementar ou uma classe específica da mesma.
Um resultado semelhante ao obtido com o uso de interfaces, pode ser obtido fazendo-se uso de uma classe abstrata, cuja implementação e uso não diferem em muito de outras linguagens, conforme a Listagem 4.
//Classe abstrata para Validar o documento namespace Polimorfismo { abstract class Validador { public virtual Boolean validarDocumento() { throw new NotImplementedException(); } } } //Pessoa Jurídica namespace Polimorfismo { class Juridica : Validador { public string Cnpj { get; set; }
public override bool validarDocumento() { return this.Cnpj.Length == 14; } } } |
Listagem 4. Uso de uma classe abstrata |
Observe na Listagem 4 as seguintes particularidades:
· Palavra reservada virtual: serve para indicar que este método é virtual e não tem estado definido até que seja executado, sendo definido apenas um corpo padrão (o qual será usado caso o método não seja sobrescrito). É de uso obrigatório caso queira-se posteriormente sobrescrever o método fazendo uso de override.
· Palavra reservada override: utilizada para dizer que o método na qual está sendo utilizada sobrescreve o método virtual de mesmo nome na classe abstrata.
· Impossibilidade de herdar de Pessoa: como Validador é uma classe (abstrata, porém uma classe), perde-se a possibilidade da classe Jurídica herdar da classe Pessoa pelo fato de o C# não aceitar herança múltipla.
Sobrecarga
Nos tópicos anteriores, ao ser tratado o assunto polimorfismo pôde-se observar a sobrescrita de métodos presentes em classes abstratas ou interfaces. Este conceito não deve ser confundido com o apresentado neste tópico, pois sobrescrita e sobrecarga são técnicas distintas.
Em orientação a objetos, uma sobrecarga refere-se aos métodos de uma classe, sendo que os mesmos podem ser sobrecarregados em relação aos seus nomes, podendo diversos métodos possuir o mesmo nome, porém a os tipos de dados da lista de parâmetros deve ser divergente, conforme pode ser observado na Figura 3.
Figura 3. Sobrecarga do método escreverNome() |
No exemplo presente na Figura 3, pode-se observar a repetição da
declaração do método escreverNome(), porém nota-se também a diferenciação de
seus parâmetros (ora sem parâmetros, ora um texto, ora um número). Esta
situação deixa clara a existência de comportamentos diferentes para um único
serviço. Esta diferenciação deve ser controlada na classe que implementa os
métodos, podendo ainda o método invocado chamar outro método de mesmo nome
(porém com lista de parâmetros diferente).
Sobrecarga com C#
A forma de implementação da sobrecarga em C# é idêntica ao ocorrido em Java, apenas redeclarando os métodos e mudando seus parâmetros, conforme pode ser observado na Listagem 5, que representa em C# a classe presente na Figura 3.
namespace Sobrecarga { class Pessoa { public string Nome { get; set; }
public void escreverNome() { Console.Write(this.Nome + "\n"); }
public void escreverNome(string titulo) { Console.Write(titulo + " " + this.Nome + "\n"); }
public void escreverNome(int vezes) { for (int i = 0; i <= vezes; i++) Console.Write(this.Nome + "\n"); } } } |
Listagem 5. Exemplo de sobrecarga de métodos em C# |
Não há a necessidade de mudar o nome dos métodos, pois o compilador sabe
diferenciar qual dos métodos da classe Pessoa foi chamado, através dos tipos e
quantidades de parâmetros.
Exceções
Quando se desenvolve fazendo uso do paradigma OO deve-se evitar o uso de blocos condicionais, como o IF, para a validação e tratamento de erros. As execuções de trechos de código que verificam se este trecho pode ser ou não executado devem ser evitadas e as instruções devem ser executadas dentro de blocos de exceção e, caso ocorra algo imprevisto, uma exceção será gerada, a qual pode ou não ser posteriormente tratada.
O uso de exceções é uma forma eficiente de propagar erros no programa em tempo de execução. Se em determinado ponto da execução do código uma exceção for lançada, a mesma deve ser tratada (corrigida) ou lançada novamente para tratamento posterior.
Uma exceção pode ser lançada pelo framework ou pelo próprio desenvolvedor, podendo o mesmo usar uma exceção existente ou criar a sua própria.
Exceções com C#
O uso de exceções em C# é muito simples, assemelhando-se com outras linguagens, como Java.
Para a criação de uma nova exceção basta criar uma classe que estenda da classe Exception (ou de outra classe de exceção mais específica), conforme sugere o exemplo presente na Listagem 6.
//Classe de exceção namespace Excecao { class Excecao : Exception //Estendendo Exception { public Excecao(string mensagem) : base(mensagem) { //Construtor que chama o construtor da classe genérica } } } |
Listagem 6. Criação de uma exceção |
Para lançar uma exceção basta instanciar um objeto da classe de exceção e dar
um throw (lançar) neste objeto, como o indicado na Listagem 7.
//Classe que lança a exceção namespace Excecao { class TestaExcecao { public void testar() { throw new Excecao("Teste"); } } }
//Classe principal que trata a exceção namespace Excecao { class Program { static void Main(string[] args) { TestaExcecao t = new TestaExcecao(); t.testar(); } } } |
Listagem 7. Lançamento de uma exceção |
Note que a exceção lançada no exemplo da Listagem 7 não foi capturada
pelo sistema, o que acaba retornando o erro Unhandled Exception (Exceção
não manuseada), porém isto ocorre apenas em tempo de execução, pelo fato do C#,
diferentemente do Java, não obrigar o desenvolvedor a capturar as possíveis
exceções.
Para capturar uma exceção, basta usar o bloco try...catch, colocando entre as chaves do bloco try o código no qual pode acontecer a exceção e criar quantas cláusulas catch forem necessárias para o tratamento dos erros, de acordo com a necessidade do desenvolvedor. Observe o exemplo da Listagem 8.
namespace Excecao { class Program { static void Main(string[] args) { TestaExcecao t = new TestaExcecao(); try { t.testar(); } catch (Excecao e) { Console.Write("Erro na Exceção de Testes\nMensagem: " + e.Message + "\n"); } } } } |
Listagem 8. Captura de uma exceção |
Conclusão
A orientação a objetos criou um novo mundo e gama de possibilidades para os desenvolvedores de sistemas exercerem sua criatividade, sendo que não se faz mais necessário preocupar-se com detalhes do funcionamento da parte física do dispositivo ou sistema operacional para o qual o mesmo está desenvolvendo uma aplicação.
Assim como outras, o C# é uma linguagem voltada a estas técnicas, que visa melhorar e tornar ainda mais fácil e direto o trabalho do usuário (desenvolvedor) da mesma, portanto, precisa-se aproveitar ao máximo os recursos por ela (e por outras também) oferecidos a fim de tratar diretamente o problema que deseja-se corrigir ou botar em prática a idéia de projeto que tem-se em mente.
Livros recomendados para informações adicionais
ARAÚJO, Everton Coimbra de. Orientação a objetos com Java simples, fácil e eficiente. Florianópolis: Visual Books, 2008. 186 p. : ISBN 97885875022269
DAMASCENO JÚNIOR, Américo Fraga,. Aprendendo ASP.NET com C#. São Paulo: Érica, 2001 212p. ISBN 8571947589
LARMAN, Craig. Utilizando UML e padrões: uma introdução à análise e ao projeto orientados a objetos e ao Processo Unificado. 2. ed. Porto Alegre: Bookman, 2004. 492p. ISBN 85-363-0358-1
DARIE, Cristian; WATSON, Karli (Autor). Beginning ASP.NET 2.0 E-Commerce in C# 2005: from novice to professional. Berkeley, CA: Apress, 2006. 681 p. (Expert"s voice in .Net.) ISBN 1590594681
MACDONALD, Matthew. Beginning ASP.NET 2.0 in C# 2005: from novice to professional. Berkeley, CA: Apress, 2006. 1148 p. (The expert"s Voice.In.net) ISBN 1590595726
Sites recomendados para informações adicionais
http://msdn.microsoft.com/pt-br/library/cc580626.aspx#
http://www.linhadecodigo.com.br/Artigo.aspx?id=1620
http://www.dca.fee.unicamp.br/cursos/PooJava/heranca/redefmet.html
http://www.dca.fee.unicamp.br/cursos/PooJava/polimorf/index.html
http://www.dca.fee.unicamp.br/cursos/PooJava/classes/metovld.html
http://www.linhadecodigo.com.br/ArtigoImpressao.aspx?id=1828
http://www.devmedia.com.br/articles/viewcomp.asp?comp=4190
http://msdn.microsoft.com/pt-br/library/ms173161.aspx
http://www.codersource.net/csharp_tutorial_exceptions.html
http://msdn.microsoft.com/pt-br/library/ms173160.aspx#