18.97.9.170
(+244) 921 543 587Ligue Já!
Ou nós ligamos!Deixe os seus dados para contacto.
Seremos breves!
Horário - dias úteis das 9h30 às 18h30
software-design-principles
{alt:Hugo Ribeiro}

Software Design Principles: do rascunho ao produto final

Na construção de software, a conceção e desenho da solução que resolve o problema em causa é a fase mais importante de todas, pois determinará, não só o sucesso do projeto, mas também o futuro do produto final e a sua resiliência a um contexto sempre em mutação. Todo o developer tem dentro de si um arquiteto de software, pronto a relevar-se à medida que o tempo passa e participe em mais projetos, adquirindo experiência, a partir dos erros cometidos.

Arquitetura

Um projeto de desenvolvimento de software inicia-se pela identificação dos problemas a resolver e a enumeração dos seus requisitos funcionais e técnicos. A partir desses requisitos inicia-se a fase de conceção da solução que, tipicamente, envolve o desenho de uma nova arquitetura – ou a aplicação de uma arquitetura base já concebida, o que acontece quando o projeto decorre numa fábrica de software, onde todos os produtos seguem a mesma lógica de construção – e a seleção das tecnologias mais adequadas. Apenas depois se inicia o desenvolvimento propriamente dito, seguido dos testes e, finalmente, a publicação do resultado final.

Um developer dirá que a fase mais importante do processo dedesenvolvimento é a programação, mas isso não é verdade. A fase mais determinante para o sucesso do projeto e para a qualidade do produto é a de conceção e desenho da arquitetura. É uma tarefa para os arquitetos de software -, aqueles developers seniores que já passaram por tantos projetos e cometeram tantos erros, que se tornaram capazes de identificar melhor as abordagens mais apropriadas, o esquema de componentes que funcionará melhor, as tecnologias mais adequadas - que conseguem antecipar cenários que surgirão, não só durante o projeto, mas muito depois da entrada do produto em manutenção e, sobretudo, que se preocupam em deixar caminhos abertos para a evolução do produto no futuro.

Arquiteturas padrão

Conceber uma arquitetura de software nunca é um exercício que se inicie a partir de uma folha em branco.

Tal como um arquiteto de pontes, por exemplo, parte de um conjunto de seis desenhos bem conhecidos e comprovados, seleciona o mais adequado ao problema e adapta-o ao caso concreto, também os arquitetos de software têm ao seu dispor um conjunto de arquiteturas base, entre as quais pode escolher o ponto de partida, a maioria das vezes em função da sua experiência e também das tecnologias disponíveis.

Eis algumas dessas arquiteturas padrão:

Client-server

Layered

Component-based

Data-centric

Event-driven

Pipes and filters

Hexagonal

Onion

Service-oriented

Microservices

Princípios de desenho

Como os design patterns (padrões de desenho) estabelecem soluções reutilizáveis para problemas (de programação) recorrentes, estas arquiteturas padrão estabelecem soluções reutilizáveis para problemas de desenho do software (como um sistema complexo) que são recorrentes em determinados contextos. Para além disto, definem o estilo da solução, a abordagem adotada para resolver os problemas em causa e, no limite, os requisitos funcionais, técnicos e tecnológicos do projeto.

Podemos assumir que um estilo de arquitetura está para a conceção do software assim como um padrão de desenho está para a sua programação. É possível utilizar todos os padrões de desenho, independentemente da arquitetura que esteja a ser utilizada, à exceção daqueles padrões que são específicos para terminado domínio (como, por exemplo, de microserviços ou de user interface).

Dito isto, o que é mesmo mais relevante nestas arquiteturas, além da sua adequação ao problema em causa? São os princípios a que a arquitetura concreta obedece ou valoriza mais, em detrimento de outros, porque serão estas ideias fundamentais que, no limite, determinarão o seu sucesso.

O sucesso de uma arquitetura é uma medida direta da qualidade do produto final, mas também – e isto é cada vez mais verdade, em particular em fábricas de software – pela sua resiliência, isto é, a sua capacidade de perdurar no tempo, evoluindo quando necessário, adaptando-se a (r)evoluções tecnológicas e à modificação do contexto (os requisitos), permitindo que o produto final seja fácil de manter e possa, ele próprio, evoluir.

Esta temática dos melhores princípios no software design está longe de estar totalmente sistematizada, quando comparada à temática dos design patterns. Ainda assim, é possível enumerar um conjunto de princípios de desenho que são mais consensuais e que estão bem presentes e vincados no desenho das frameworks e produtos mais modernos.

SOLID

Os princípios de desenho mais reconhecidos atualmente estão associados ao acrónimo SOLID.Esses princípios de programação orientados a objetos "não são regras, nem verdades absolutas, são apenas bons conselhos, heurísticas, soluções de senso comum”.

Single Responsibility Principle

A class should have one, and only one, reason to change.

Este princípio visa facilitar a implementação do software e prevenir efeitos secundários inesperados de futuras alterações. Na sua essência, cada classe deve ter uma única e clara responsabilidade, que conduzirá, necessariamente, a alterações menos frequentes, a um código mais simples e organizado e à modularidade como preocupação primária na conceção da arquitetura.

Open-closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

As linguagens de programação orientadas a objetos possuem em si os artefactos necessários para construir código extensível com facilidade. Todavia, estes mecanismos, só por si, não garantem que o resultado é simples, fácil de compreender ou fácil de evoluir.

Este princípio defende a utilização de interfaces, no lugar de superclasses, definindo o comportamento aberto, ou seja, aquele que pode ser estendido. Assim, a interface é aberta e as classes que a implementam são fechadas.

Liskov Substitution Principle

Let f(x) be a property provable about objects x of type T. Then f(y) should be true for objects y of type S where S is a subtype of T.

Ao analisar profundamente o princípio open/closed, verificamos que o princípio da substituição de Liskov se foca no comportamento das classes envolvidas numa relação de herança, a superclasse e as subclasses. Define que as instâncias de objetos da superclasse devem poder ser substituídas por instâncias de qualquer das subclasses sem causar quebras na aplicação. Desta forma, tem como objetivo último tornar o software mais fácil de alterar sem implicações inesperadas.

Interface Segregation Principle

Clients should not be forced to depend upon interfaces that they do not use.

Mais uma vez, este princípio procura reduzir a frequência das alterações necessárias ao código e os efeitos secundários dessas alterações, promovendo a sua divisão em partes mais independentes. A ideia essencial passa por evitar a poluição de interfaces com propriedades e métodos que apenas serão necessários em parte das suas implementações concretas, optando antes pela construção de interfaces adicionais.

Dependency Inversion Principle

High level modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.

Este princípio defende que os módulos de mais alto nível – implementam a lógica de negócio mais complexa – não devem ser afetados por alterações nos módulos de baixo nível – que por sua vez implementam lógica mais simples e utilitária. A independência é conseguida através da introdução de uma abstração entre os dois. Enquanto que esta abstração não for alterada, será possível alterar os módulos de alto nível sem afetar os de nível inferior e vice-versa.

Software Design: mais princípios

Existem muitos outros princípios de software design que estão a ser, progressivamente, mais adotados. Muitos deles descrevem abordagens de mais alto nível, em comparação com os 5 princípios SOLID e, por isso, determinam muito mais os princípios de uma arquitetura do que dos seus componentes.

Vale a pena enumerar alguns desses princípios menos concretos.

KISS (Keep It Simple Stupid)

Este princípio é válido para o software como é para qualquer outra indústria, profissão ou atividade. Quanto mais complexo é algo, mais formas de falhar existem, mais difícil será explicar o seu funcionamento e mais custoso será alterá-lo. Como dizia Einstein: "Everything should be made as simple as possible, but not simpler”.

YAGNI (You Ain’t Gonna Need It)

Determinada funcionalidade/componente/classe/método deve ser sempre implementada quando é necessária, nunca quando se espera que venha a ser necessária. Objetivamente, deve reduzir-se o trabalho desnecessário e adiar sempre a decisão de implementar algo para o mais tarde possível.

DRY (Don’t Repeat Yourself)

É consensual que a duplicação representa desperdício e traduz-se em custo (na manutenção, modificação, etc.), sendo o princípio DRY defensor desta máxima:

A duplicação na lógica do software deve ser eliminada por via de abstrações (interfaces, herança, etc.).

A duplicação nos processos (testes, builds, etc.) deve ser eliminada através da automação.

Once and Only Once

Este princípio é uma especialização do anterior e acaba por ser um dos mais fundamentais, afirmando que qualquer comportamento de uma aplicação deve ser implementado uma única vez.

IoC (Inversion of Control) ou Hollywood (Don’t call us. We’ll call you.)

O princípio Hollywood ilustra uma forma diferente de programar o software em comparação com a visão tradicional em que o código dita o fluxo de controlo. Quando aplicado, o código é escrito para responder a eventos externos, tipicamente da framework em que o software assenta.

Separation of concerns

É uma generalização do Single Responsibility Principle. Enquanto este último aborda a separação de responsabilidades das entidades de código mais elementares (classes, interfaces, métodos), o princípio Separation of Concerns centra-se na arquitetura de alto nível, nos componentes, nas camadas e nos respetivos módulos. O objetivo é o mesmo: promover a modularidade, reduzir as alterações necessárias e minimizar o seu impacto.

Encapsulation

O encapsulamento no software resulta numa ideia nuclear de qualquer arquitetura: as "caixas negras”. Cada objeto, componente ou módulo deve ser dono (e único responsável) pelo seu comportamento e estado. Os seus clientes e colaboradores devem desconhecê-los e não dependerem nunca da sua implementação interna.

Será provavelmente um dos princípios de desenho mais valorizados pelos arquitetos e aquele que provavelmente motivou a primeira ideia de construir software a partir de análise técnica e de um desenho de arquitetura. É muito mais difícil de atingir do que pode parecer à primeira vista e existem muitas forças, assim como tendências mais recentes na indústria, que têm levado a que venha perdendo preponderância em benefício de outras abordagens (como a injeção de dependências, por exemplo).

Engenharia de software

A construção de software é uma disciplina de engenharia, não é uma atividade criativa, ainda que possa beneficiar muito de genialidade e invenção. Como tal, deve reger-se por conceitos, princípios, métodos, técnicas e ferramentas, cujos resultados possam ser medidos e avaliados. Uns produzirão melhores resultados que outros, dependendo sempre do contexto de aplicação e dos problemas que o software (cada produto, cada projeto) deve resolver.

Os princípios de desenho aqui enumerados definem boas práticas que devem ser consideradas na conceção das arquiteturas. Como os padrões de desenho, os princípios de desenho são abordagens para problemas frequentes, sendo o seu objetivo eliminar desperdícios, potenciar a qualidade e maximizar a produtividade.

A padronização é um caminho indispensável para a indústria do software, o que não retira espaço para a contribuição individual de cada developer, tester ou arquiteto de software. Pelo contrário, dá lhes competências adicionais para fazerem o seu trabalho especializado e de maior valor: a resolução de problemas e a conceptualização de soluções e sistemas complexos.

Receba a newsletter com as nossas melhores histórias!