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újo



Este é 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#

Everton Coimbra de Araújo

Everton Coimbra de Araújo - Desde 1987 atua na área de treinamento e desenvolvimento. Como Mestre em Ciência da Computação, é professor da UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ, Campus Medianeira, onde leciona disciplinas relacionadas ao desenvolvimento de aplicações web, com Java e .NET.

É autor dos livros Desenvolvimento para WEB com Java, Orientação a Objetos com Java - Simples, Fácil e Eficiente, Algoritmo - Fundamento e Prática em sua terceira edição e dos livros Delphi - Implementação e Técnicas para Ambientes Virtuais e C++ Builder - Implementação e Técnicas para Ambientes Virtuais. Todos pela VisualBooks. Pode ser contactado através do e-mail everton@utfpr.edu.br ou evertoncoimbra@gmail.com.