Introdução
Hoje em dia, quando falamos de programação de software, inevitavelmente estamos a falar de Programação Orientada a Objectos (POO). Torna-se pertinente perguntar o que é o design orientado a objectos (OO)? É sobre o quê? Quais os seus benefícios? Quais os seus custos? Apesar de virtualmente todos os programadores de software utilizarem uma linguagem Orientada a Objectos (OO), de alguma forma, muitos de nós utiliza estas linguagens sem saber porquê ou sem saber como retirar o máximo benefício delas.
De todas as revoluções que ocorreram na indústria do desenvolvimento de software, duas tiveram tanto sucesso que moldaram a nossa mentalidade de modo que as tomamos como um dado adquirido: Programação Estruturada e Programação Orientada a Objectos. Todas as principais linguagens de programação que usamos hoje em dia são fortemente influenciadas por estas duas disciplinas.
Mas, o que é necessário para fazermos Programação Orientada a Objectos?
Bem, podemos começar por dizer que temos que usar uma linguagem Orientada a Objectos (OO), como, por exemplo, Java, C#, C++, VB.NET. Mas só porque estamos a usar uma linguagem OO, só porque estamos a programar com classes, não quer dizer que estejamos a fazer POO. Ou pelo menos, não quer dizer que o estejamos a fazer da forma correcta. Muitas vezes os programadores não têm conhecimento dos princípios e fundamentos que são a base das disciplinas de que a linguagem de programação que estão a usar derivou.
Este artigo é assim sobre as bases da POO, sobre os Princípios da Programação Orientada a Objectos. Em específico, vamos analisar os princípios S.O.L.I.D., definidos por Robert C. Martin.
Conteúdo
- The Single Responsability Principle (Princípio da Responsabilidade Única)
- The Open/Closed Principle (Princípio do Aberto/Fechado)
- The Liskov Substitution Principle (Princípio da Substituição de Liskov)
- The Interface Segregation Principle (Princípio da Segregação de Interfaces)
- The Dependency Inversion Principle (Princípio da Inversão da Dependência)
Estes princípios focam-se nos aspectos de gestão de dependências da POO, i.e. a gestão do Acoplamento entre os módulos do software. O alto acoplamento é um problema que a maioria de nós já enfrentou. Sempre que temos que analisar algum pedaço de código legacy, somos confrontados com má gestão de dependências, resultando em software que é difícil de alterar, frágil e não reutilizável. Por outro lado, quando as dependências são bem geridas, resulta código com baixo acoplamento e o software é flexível, robusto e reutilizável. Então a gestão de dependências e este princípios, estão na fundação das características e facilidades que os programadores pretendem que o software tenha.
The Single Responsability Principle (Princípio da Responsabilidade Única)
Uma classe deve ter uma e apenas uma razão para mudar. Basicamente, isto significa que cada classe deve ter uma única responsabilidade e que a responsabilidade deve estar totalmente encapsulada pela classe. Todos os seus serviços devem estar estreitamente alinhados com essa responsabilidade.
Por exemplo, se criarmos uma classe que represente uma Encomenda, não queremos que essa classe faça a persistência dos dados para BD e que os exporte também para XML. Porquê? Porque se, mais tarde, quisermos mudar de BD ou alterar o esquema do XML, estamos a possibilitar que a alteração de uma responsabilidade obrigue à alteração de outra responsabilidade.
The Open/Closed Principle (Princípio do Aberto/Fechado)
As entidades de software (classes, módulos, funções, etc.) devem estar abertas para extensão, mas fechadas para modificação ou, dito de outra maneira, devemos ser capazes de estender o comportamento de uma classe, sem a modificar.
Em princípio, isto parece contraditório: como podemos fazer um objeto comportar-se de uma forma diferente sem o modificar? A resposta: usando abstrações, ou colocando o comportamento (responsabilidade) em classes derivadas. Por outras palavras, ao criarmos classes base com funções “overridable” (que permitem substituição), podemos criar novas classes derivadas, que fazem a mesma coisa de forma diferente, sem alterar a funcionalidade base. Mais, se as propriedades da classe abstracta necessitam ser comparadas ou organizadas em conjunto, uma outra abstração deve lidar com isso. Esta é a base do argumento "manter privadas todas as variáveis do objeto" (Encapsulamento).
The Liskov Substitution Principle (Princípio da Substituição de Liskov)
As classes derivadas devem ser substituíveis pelas suas respectivas classes base. Por outras palavras, métodos que usem referências a classes base, têm que ser capazes de usar objectos de classes derivadas, sem o saber.
Quando se faz a sobreposição de um método duma classe abstracta, este tem que ser implementado de forma correcta, na classe derivada. Ou, “quando se usa um objecto através do interface da sua classe base, o objecto derivado não deve esperar que essa utilização obedeça a pré-condições que sejam mais fortes do que aquelas requeridas na classe base". A ilustração sempre popular desta caraterística é o exemplo quadrado-rectângulo.
Nota: este princípio deve o seu nome a Barbara Liskov, que o enunciou inicialmente numa conferência em 1987.
The Interface Segregation Principle (Princípio da Segregação de Interfaces)
Devem-se refinar e criar interfaces específicos para cada cliente, ou, dito de outra forma, os clientes não devem ser forçados a depender de interfaces que não usam. Quando um cliente depende de uma classe que implementa interfaces que o cliente não usa, mas que são usados por outros clientes, então esse cliente vai ser afectado pelas mudanças que os outros clientes possam forçar na classe.
The Dependency Inversion Principle (Princípio da Inversão da Dependência)
Devemos depender de abstrações e não de concretizações. Abstrações não devem depender de detalhes. Os detalhes é que devem depender de abstrações. Isto está intimamente relacionado com o Princípio Aberto/Fechado, discutido anteriormente. Ao passar dependências (tais como conectores a dispositivos de armazenamento) para as classes, como abstrações, removemos a necessidade de programar para uma dependência específica.
A utilização de Dependency Injection é uma forma de seguir este princípio.
Este artigos apresenta apenas uma breve introdução os princípios SOLID, pretendendo destacar a utilidade e vantagens da sua aplicação. Para um conhecimento mais aprofundado sobre o tema, recomenda-se a pesquisa e leitura da extensa bibliografia online.