Desenvolvimento - Modelagem

Programação a Objetos - Nível Zero

Veja neste artigo uma introdução prática aos princípios de orientação objetos, focado na abstração dos objetos de uma aplicação qualquer. Tem como objetivo dar ao programador iniciante algumas percepções de como definir a estrutura de suas classes de uma aplicação POO.

por Flávio Henrique de Carvalho



“O único modo de evitar os erros é adquirindo experiência; mas a única maneira de adquirir experiência é cometendo erros.”

Venho programando orientado à objetos, criando aplicações utilizando um modelo de domínio, separando em camadas as regras de negócios, da interface de usuário, da camada de infraestrutura, aplicando padrões de projetos, utilizando o padrão de arquitetura MVC e considero que estas e outras características fazem parte de um pacote de boas práticas para se construir sistemas comerciais atualmente. Mas o alicerce básico para a aplicação destas práticas e técnicas vêm do paradigma de Programação Orientada à objetos, POO, sem o qual tudo isto perderia um pouco ou totalmente o sentido. E a pergunta que este post tenta responder é como iniciar o processo de criação de aplicações POO?

De uma forma geral, o aprendizado de programação inicia-se através do paradigma de Programação Imperativa, onde um programa é uma sequência de ações, que através de comandos e de declarações de variáveis realiza uma ação específica. Ou seja, o código fonte é uma sequência de instruções escrita de forma a implementar um algoritmo para a realização de alguma tarefa. E pela própria naturalidade da programação imperativa e sua similaridade com as linguagens naturais, torna-se muito mais didático adquirir “lógica de programação” através dela, do que utilizando outro paradigma. Acredito que mesmo os profissionais que aprendem a programar em linguagens com suporte a orientação a objetos, iniciam construindo programas imperativos. E o que é pior, as vezes criando a falsa sensação de que por estar escrevendo código dentro de uma classe, esteja se fazendo POO.

Para treinar a criação de algoritmos, podemos criar um código que se aproxime do que será visto em linguagens de programação, ou seja, podemos criar um pseudocódigo, que é um dos tipos de representação de algoritmos.

Existe um processo de transição entre estes dois paradigmas, de programação imperativa para a Programação orientada a objetos, que deve ser assimilado. Não querendo de forma alguma vender a ideia de que POO seja alguma espécie de evolução de qualquer outro paradigma, pois cada um tem seu nicho de aplicações específicas. Porém, a evolução para construção de aplicações imperativas para POO não é instintiva, esta vem de embasamento teórico e de prática, muita prática. O fato é que o simples entendimento dos conceitos de POO muitas vezes adquiridos em disciplinas acadêmicas, cursos de programação, ou advindo de alguma literatura, não habilita a construção de boas aplicações POO. Sendo pragmático, acredito que só se criam aplicações POO, programando em POO e não lendo ou estudando POO. Muito embora este pareça ser um raciocínio contraditório, esta explicação está fundamentada no próprio conceito da prática de se fazer ciência. Que num sentido estrito, ciência refere-se ao sistema de adquirir conhecimento baseado em juntar evidências empíricas verificáveis, baseada na observação sistemática e controlada, geralmente resultante de experiências ou pesquisa de campo e analisá-las com o uso da lógica. Vem do poder da observação, da experimentação, que se adquirem lidando com problemas reais em um cotidiano do trabalho.

O entendimento de conceitos como herança, polimorfismo, encapsulamento, agregação, composição, uso de interfaces são importantes, mas não suficientes para a construção de sistemas POO na prática. Ao aprender a codificar nossos primeiros sistemas surge a falsa percepção de que tudo flui facilmente, mas numa fase posterior, muitas vezes percebe-se que quase simplesmente estamos migrando código imperativo, apenas encapsulando-o em alguma classe qualquer. Faltando o principal em POO, que é a percepção da existência dos objetos dentro do código, a abstração das entidades existentes no domínio do sistema, cuja identificação é a alma da POO.

Não se aprende nos livros de escola a identificar objetos que comporão o domínio (regras de negócio) de um sistema. Os exemplos clássicos da classe Carro e sua agregação com a classe Rodas, ou da classe Cliente e sua associação com a classe Pedidos, são meros exemplos didáticos que não contribuem na prática para identificação de objetos “reais” em sistemas “reais”. Num cotidiano de trabalho muitas vezes passamos por linhas e linhas de códigos e não vemos os objetos literalmente escondidos nas entrelinhas. Isto vem da falta de intimidade com os conceitos de nossas regras de negócios e da inabilidade de garimpar tais objetos dentro do código, resultando em mais e mais amontoados de objetos “invisivelmente” encapsulados dentro de outros objetos. Talvez haja alguma facilidade em criar classes de infraestrutura como EnvioDeEmails, ou ConexaoBancoDeDados, ou ImportadorDePlanilhasExcel, mas abstrair objetos do domínio é outra história. Os designs das classes de uma aplicação não surgem num passe de mágica ou num momento de inspiração, mas no total envolvimento com os conceitos das regras de negócio e no decorrer do processo cotidiano de implementação de um projeto. Num primeiro momento é possível esboçar as classes, seus atributos e alguns métodos que comporão o projeto, apenas esboçar. Mas até mesmo pelas constantes mudanças de requisitos nas regras de negócios não acho possível uma definição completa das classes de uma aplicação numa fase inicial do processo de construção da aplicação.

Portanto, é preciso criar objetos! Mas antes de criá-los é preciso identificá-los. E uma das técnicas para esta identificação consiste no capricho e tempo despendido em nomear nossas classes, nomear seus campos, suas propriedades e métodos (o livro Código Limpo, do Robert C. Martin, trata com mais detalhes deste assunto). Não é perda de tempo gastar alguns minutos pensando num nome adequado para objetos e seus membros, isto é realmente um grande investimento na identificação e caracterização de objetos. Após resolver dar nomes melhores às minhas classes e métodos comecei a perceber pelo nome que eu dava que alguns métodos não se encaixavam na classe em que estavam. Seguindo a regra de que um método constitui uma ação, portanto nada melhor que seu nome iniciar-se com um verbo (Load..., Get..., Set..., etc.). Devendo-se observar se a ação que o método executa combina com seu nome, que por sua vez combina com a classe onde está contido. Bons nomes também ajudam com os campos e propriedades, tornando mais fácil a percepção da criação ou não de outro objeto caso necessário. Isto é tão sério, que acho que classes deveriam ter registro em cartório.

Percebo por mim mesmo que existe certa preguiça em se criar objetos. Acho que mesmo que seja para a inserção de apenas um único método deve-se criar um objeto que o receba, caso fique caracterizado o um novo conceito por trás dele. Seguindo o princípio de que uma classe tem APENAS UM PAPEL, assim como um método realiza APENAS UMA TAREFA, é possível surgir em diversas linhas de códigos uma explosão de objetos ainda não abstraídos de um código relativamente grande. Via de regra, os objetos devem assumir um só papel, podendo servir de transporte de dados (DTO’s), representar um objeto cadastral do banco de dados, realizar alguma tarefa envolvendo outros objetos (um serviço), persistir outros objetos no banco de dados, entre outras funções. E caso seja identificado mais de um papel dentro de uma mesma classe, isto implica que existe mais de um objeto inserido no mesmo contexto, portanto é preciso desmembrá-los. Uma falha grave é a não percepção desses “papéis”, a não caracterização de cada objeto leva ao ajuntamento de várias tarefas dentro de uma mesma classe, criando-se verdadeiros Frankenstein’s cheios de pedaços num corpo só. Classes e métodos grandes devem ser refatorados e analisados.

Outro fenômeno interessante causador de problemas na percepção das classes, vem da repetição de código e/ou a repetição de uma mesma ideia dentro de um trecho de código. Identificar um código repetido e encapsulá-lo em um método genérico pode ser uma tarefa fácil, mas a percepção da repetição de uma mesma tarefa dentro de um código ligeiramente diferente pode ser uma tarefa mais difícil de ser perceber. Códigos repetidos e ideias repetidas muitas vezes apontam para uma característica em comum entre objetos, que devem ser identificados e devidamente tratados. Uma boa prática é associar tal código ou tal ideia a uma classe abstrata e fazer com que os objetos que possuem tal repetição de código implementem esta classe, caso sejam classes similares dentro de um contexto.

Dentro de uma aplicação onde existe uma hierarquia de camadas existem classes para as mais variadas finalidades e diversos graus de visibilidade dentro do código, classes internas, privadas, públicas. Um código onde todas ou quase todas as classes estão definidas como públicas pode indicar sintomas de problemas no design. Pois à medida que nossos objetos são abstraídos numa granularidade satisfatória, surgem objetos cujo escopo é restrito a uma pequena área da aplicação, sendo simplesmente objetos auxiliares em alguma tarefa simples. Ou também objetos menores que servem apenas para compor partes de outros objetos. E o fato de se ter um percentual muito alto de classes públicas dentro de todas as camadas, além da questão arquitetural, pode estar ocultando estes objetos menos elaborados. Isto não é uma regra, depende muito da concepção do design e também do tamanho da aplicação.

Não existe uma receita geral para fazer boas aplicações, muito menos orientadas à objetos. Uma aplicação pode ser concebida utilizando de várias formas e possibilidades e todas serem boas implementações do domínio. Também não defendo a ideia de que aplicações boas só devem ser escritas no paradigma POO, pois temos Programação Orientada a Aspecto, Programação Procedural, Programação Funcional, etc, sendo cada paradigma voltado para uma finalidade. Talvez até pareça existir um certo exagero nesta busca extrema pela abstração de objetos dentro de uma aplicação, mas considero que a clareza de nossos objetos e a clareza com que se interagem é o grande ganho disto tudo. E uma coisa é mais que certa, não existe programação orientada a objetos sem objetos.

Flávio Henrique de Carvalho

Flávio Henrique de Carvalho - Bacharel em Ciência da Computação pela universidade Paulista de Ribeirão Preto. Trabalha com desenvolvido na plataforma Microsoft à 10 anos, para o desenvolvimento de aplicações de pesquisa na área de engenharia florestal. Focado em aplicações estatísticas e processamento de grandes massas de dados.

Visite seu blog,
http://flaviohenriquedecarvalho.wordpress.com.