De que se trata o artigo

Da aplicação do desenvolvimento orientado a objetos, seus pilares e boas práticas, utilizando recursos nativos do Delphi XE 2.


Em que situação o tema é útil

Entender a orientação a objetos como um todo, entender o que o Delphi XE 2 oferece sobre o assunto e como aplicar em um projeto Delphi XE 2.

Desenvolvimento OO no Delphi XE 2

A orientação a objetos é ensinada nas faculdades, utilizando na grande maioria das vezes outra linguagem que não é o Delphi, mesmo assim, o que é ensinado não alcança o que um desenvolvimento real exige. Neste artigo vamos abordar a orientação a objetos como um todo e veremos através de um exemplo como o Delphi XE 2 nos ajuda agora a aplicá-la.

O desenvolvimento orientado a objetos fascina muito desenvolvedor Delphi, mas às vezes por insegurança ou falta de prática mesmo, não o aplica em sua totalidade. Muitos só viram orientação a objetos na faculdade, e em outra linguagem. Ainda existe a situação onde muitos desenvolvedores iniciantes nem se dão conta que um formulário é uma classe que deriva de TForm (classe base de formulários), que um componente TButton é uma classe também e assim por diante.

O Delphi já nasceu orientado a objetos, mas sua natureza RAD acaba escondendo sua estrutura orientada a objetos. É muito mais prático arrastar e soltar do que criar uma estrutura que separe a UI das regras de negócio. A partir do Delphi XE 2 isso começa mudar. Recursos como o LiveBinding tiram do meio do caminho o grande empecilho e desculpa que havia: como vou ligar meus objetos à tela? Agora é possível utilizar classes de negócio (que entenderemos no decorrer do artigo) como fonte de dados reais.

Para se desenvolver um bom software orientado a objetos devem-se seguir algumas boas práticas, estratégias e organização que veremos a seguir e só então partiremos para a construção do exemplo.

Princípios de um sistema orientado a objetos

Sempre que encontramos um sistema de muita manutenção tentamos resolver seus problemas imaginando que uma melhor coleta de dados junto ao usuário resolva o problema e o sistema se torne mais fácil depois disso. Existem problemas que vão além disso, problemas esses que estão localizados na estrutura do projeto. Existe um fato único que não muda: requisitos de sistema sempre mudam. Assim, é melhor preparar o desenvolvimento para isso, melhorar a coleta de informações ajuda, mas não resolve. O desenvolvedor deve criar o pensamento de “como escrever um código que se adapte fácil e rapidamente às mudanças que os clientes solicitam”. Para ajudar, existem alguns princípios: Modelar para interfaces, favorecer composição sobre herança e encontrar o que varia, e encapsulá-lo.

Modelar para interfaces

Existem dois problemas que impedem um sistema de ser facilmente alterado quando há uma mudança em seus requisitos: baixa coesão e alto acoplamento.

. Baixa coesão: Isso é detectado quando encontramos rotinas/classes que realizam várias funções e essas são dependentes entre si, ou seja, é o quanto as operações de uma rotina/classe dependem de outras;

. Alto acoplamento: Encontramos um alto acoplamento quando temos rotinas/classes que são muito dependentes de outras, aonde um alteração nessa conduz a um cascateamento de alterações, ou seja, é o quanto as rotinas/classes estão dependentes uma das outras.

Para amenizar, ou evitar esses problemas, pode-se aplicar o conceito de modelar para interfaces. Interface aqui não é a interface gráfica com o usuário do seu sistema, e sim o conjunto de operações, propriedades e atributos que compõe uma determinada classe.

É através de sua interface que uma classe se faz conhecida e interage com outras classes. Para aplicar esse conceito, podem-se utilizar heranças iniciem a partir de classes abstratas, que nada mais são do que interface pura, já que seus métodos são implementados por seus descendentes, ou o tipo interface mesmo que oferece vantagens como possuir um contador de referências que, evita falhas no gerenciamento de memória.

Favorecendo composição sobre herança

Em sistemas orientados a objeto, para se conseguir reutilização de funcionalidades, são utilizadas duas técnicas em especial: herança e composição. A herança permite que o interior de uma classe base seja vista pelas suas subclasses, isso caracteriza a herança como uma técnica white box, ou “caixa branca”.

A composição concede reutilizações mais complexas e exige que os objetos envolvidos possuam interfaces bem elaboradas. Quando a composição é utilizada em alternativa à herança, o interior dos objetos não fica visível entre si e devido a isso é caracterizada como black box, ou “caixa preta”.

A herança permite que de forma fácil seja alterada a implementação de uma funcionalidade. Isso é obtido através da técnica de sobrecarga de operações. Um problema com esse tipo de reutilização está no fato de ser resolvido em tempo de compilação, levando certo enrijecimento ao modelo, porque em tempo de execução não é permitido alterar qualquer implementação de operações de uma classe base.

Os mais puristas em orientação a objetos dizem que a principal desvantagem da herança está na exposição do interior de uma classe base à suas subclasses, quebrando assim o conceito de encapsulamento. Essa exposição conduz a um modelo de dependência de implementações entre as classes envolvidas, isso porque a parte exportada da classe base faz parte também das subclasses.

Qualquer alteração na classe base implica em alterações nas subclasses, o que não é recomendado quando se deseja um modelo flexível. A composição de objetos exige que as interfaces sejam respeitadas, consequentemente devem ser bem planejadas para que não haja impedimentos de reutilização, isso porque a implementação de composição é feita através de referências.

Algumas desvantagens vistas na herança, como quebra de encapsulamento e dependência de implementação, não são vistas na composição, isso porque os objetos envolvidos são acessados somente por suas interfaces.

O impacto disso na modelagem, é que classes manterão seu encapsulamento e estarão voltadas para sua tarefa apenas, porque não há o que herdar de outras classes, obedecendo assim o conceito de alta coesão.

Porém, como nada é perfeito, o uso de composição prende o comportamento do modelo aos relacionamentos entre os objetos, que pode implicar em maior complexidade.

Encontrar o que varia e encapsular

Tradicionalmente o encapsulamento é visto apenas como “ocultação de dados”, porém é mais do que isso, e para exemplificar vamos utilizar a seguinte analogia: “Tomando o conceito de que um guarda-chuva é algo que nos protege da chuva, um automóvel também poderia ser chamado do guarda-chuva”. Essa visão não está errada, porém limita o que é um automóvel, da mesma forma acontece com encapsulamento. Podemos encapsular além de dados, qualquer implementações, classes derivadas e comportamentos.

...
Quer ler esse conteúdo completo? Tenha acesso completo