Princípios da Programação
Programming computer screen
Geral

Há certas regras universais e principios no desenvolvimento de software que guia os engenheiros que trabalham na àrea. Nesta pagina tentamos decompor estes prinicipio enumerandoos e explicando cada um em suma.

KISS

(Keep it Simple Stupid) ou (Keep it Short and Simple)
Em Português traduz se Mantém-no Estupidamente Simples.

Porquê?

  • Menos codigo significa menos tempo a escreve-lo, tem menos bugs e é de mais facil manutenção.
  • Pode se dizer que no código e levando este principio à risca podemos dizer que o codigo esta completo não quando não falta adicionar nada mas sim quando já não dá para tirar nada.
Recursos:

YAGNI

(You ain't gonna need it)
Em Português traduz se Não vais precisar disso.

Porquê?

  • Não adianta perder tempo numa coisa que só irá ser implementada numa futura versão.
  • Torna o codigo confuso e "gordo" para uma função do softaware que ainda não foi implementada e que, provavelmente com alguma mudança de planos, não irá ser implementado da forma que hoje se pensa que vai, tornando-se inutil o esforço.
Como?
  • Sempre tenta implementar apenas o que é preciso sem fazer futurologia, ou seja, implementar coisas que no futuro irá ser implentado.
Recursos

Separação de Responsabilidade

A separação de responsabilidades em programação é um principio que define que o programa deve ser separado em secções distintas cada secção, ou parte, deve ter apenas uma responsabilidade e não ter conhecimento ou interligação com outras, ser uma parte isolada do todo. Ou seja uma alteração à interface gráfica do programa não deve influenciar em nada o codigo em que o algoritmo de BackEnd funciona e vice-versa.

Porquê?

  • Simplifica o desenho e manutenção do sistema.
  • Quando bem desenhado permite que as secções independentes sejam atualizadas e expandidas sem ter que fazer alterações a outras partes do projecto.
  • Evita que ao atualizar codigo, por exemplo, na ligação à base de dados façamos com que outra parte distinta como uma regra de negocio deixe de funcionar.
  • Como? *
  • Programar e dividir sempre o programa em modulos cada um com a sua função bem definida.

DRY

Não te repitas! (Don't repeat yourself)

Cada modulo de uma aplicação, deve ter uma função distinta como descrito no principio acima Separação de Responsabilidades. Além disso é necessário que não haja repetições.

Porquê?

  • Imagine um programa em que numa secção deixa os utilizadores registarem-se e inserir os seus dados na BD. Se mais tarde você fizer uma aplicação para esse mesmo programa e tiver que fazer novamente o codigo para o utilizador se registar na base de dados esta-se a repetir e isto pode levar a muitos bugs e um pesadelo para descobrir o que se passa no codigo porque toda a gente espera que a inserção de novos utilizadores seja feito apenas por um modulo. Além disso cada vez que você tiver que alterar o codigo em que o utilizador é inserido irá ter que o alterar em N lugares onde o criou.
Como?
  • Seguindo os principio acima é facil conseguir isto, basta não se repetir. No problema acima basta criar um modulo que recebe a info dos utilizadores e insere na base de dados, quando e se for preciso, ser alterado, apenas este modulo é alterado e subsituido não precisa de procurar onde há código que o faz.
  • Identificar cada uma da responsabilidade de cada modulo do software e criar os modulos reusaveis e com nomes bem explicitos da sua função no sistema para que mais tarde seja facil os encontrar, reusar ou atualizar.

Crie codigo para quem o vai manter.

Sempre desenhe o seu codigo ou projecto para que seja facil voltar a pegar nele. Seja você ou outra pessoa. Porque mesmo quando é você a voltar a pegar no codigo, posso lhe garantir que passados alguns meses e depois de ter mexido noutros projectos você não se vai lembrar do que aquela função xyz(); abreviadissima fazia, obrigando o a abrir o ficheiro e ler e contaxtualizar a função. Multiplique isto por varias funções ou classes com o nome abreviadissimo e veja o tempo que perde só para começar a trabalhar!

Evite otimizações prematuras.

Foque-se em desenhar o software e que ele funcione bem e deixe a rapidez de lado por enquanto, caso realmente seja necessário você fará isso mais tarde até para ter com o que comparar. A otimização prematura é um dos piores "males" da programação.

Porquê?

  • Antes de ter o software desenhado como você tem a certeza de onde ele é limitado ou lento?
  • Codigo otimizado é normalmente mais dificil de ler e manter.
Como?
  • Apenas se preocupe com a velocidade quando estiver definido onde o seu programa esta lento e porquê.

Minimize o emparelhamento

Como já foi referido em cima cada parte do seu programa deve ter uma função e apenas um objectivo bem definido e declarado. Codigo entrelaçado entre modulos mais tarde ou mais cedo irá dar problemas.

Considere a seguinte função:

public void insertStatsForTheDay(Db connection){
Stats stats = StatsGenerator.getStats();
connection.insert(stats);
**connection.close();**
}

Na função acima é obvio que ela faz uma coisa a mais que é fechar a conexão com a BD a responsabilidade dela era apenas inserir as estatiticas ninguem espera que depois de usar esta função terá que reabrir a conexão com a base de dados. Quem mais tarde vier a mexer neste codigo no meio de muitos ficheiros de codigos boa sorte a descobrir porque a conexão às vezes se encontra fechada sem razão aparente.

Lei de Deméter

Principio do menor conhecimento possível

É um principio de programação em que os objetos não devem ter mais conhecimento do que o realmente necessário. Isto para reduzir o emparelhamento. Imagine que o objecto A usa o objecto B e que este ultimo contém uma instancia do objecto C. Se o objecto A precisa de usar uma função do objecto C não deve usar o objecto B para lá chegar porque isso faz com o seu programa tenha muitas dependencias cruzadas e uma simples alteração em qualquer um dos objectos poderá obrigar a que se tenha que alterar os 3 em efeito cascada. Para evitar isso o objecto C deve ter uma interface que o deixe ser acedido pelo objecto A sem o B ter conhecimento do acesso.

No exemplo do método insertStatsForTheDay(Db connection); só é possivel aquele objecto fechar a conexão porque lhe é passado um objecto da Base de Dados. Se tivesse sido criado um interface ou o metodo de close connection tivesse protegido o bug poderia ocorrer na mesma mais iria ser muito mais facil de detectar.

Recursos:

Liskov Substitution Principle

Objectos num programa devem ser substituiveis uns pelos outros casos descendam do mesmo objecto mãe. E todas as implementações devem servir aos descendentes não deve haver casos em que uma classe filho não consiga executar codigo herdado pela hierarquia.

Interface Segregation Principle

Segregação de interfaces

O interfaces devem ser reduzidos ao minimo indespensavel. Assim como no Liskov Substitution Principle todos os metodos do interface devem ser utilizados e fazer algo de útil se há classes que implementam o interface mas não usam todos os seus metodos das duas uma ou não devem implementa-lo ou o interface pode ser dividido em dois para que tudo seja utilizado em pleno pelas classes que o implementam.

Considere o seguinte mau exemplo:

public abstact class Passaro{
public abstract void comer();
public abstract void voar();
}

public class Pinguim extends Ave {
@Override
public void comer(){
System.out.println("Este pinguim esta a comer");
}
@Override
public void voar(){
throw new RuntimeException("Os pinguins não voam! :(");
}
}

Apesar de muitas aves voarem nem todas o fazem criar o metodo public abstract void voar() na classe mãe é estar a pedir problemas que irão aparecer mais tarde. Para contornarmos o problema o melhor que fazemos é criar um interface que será implementado pelas aves que voam, no nosso caso o pinguim não o implementará mas um papagaio, poderá faze-lo.

Deste modo fariamos o nosso programa teria o seguinte:

public abstact class Passaro{
public abstract void voar();
}
public interface Voa{
public void voar();
}
public class Pinguim extends Ave {
@Override
public void comer(){
System.out.println("Este pinguim esta a comer");
}
}
public class Papagaio extends Ave implements Voa {
@Override
public void comer(){
System.out.println("Este papagaio está a comer");
}
@Override
public void voar(){
System.out.println("Este papagaio está a voar");
}
}

Deste modo apenas as aves que voam implementariam o interface "Voa" e não teriamos que andar sempre a evitar aquele metodo que não serve a todas as implementações de Ave.

Conclusão

Este post foi criado para dar uma ideia e bases nos Principios Principais na Programação Orientada a Objectos ao mesmo tempo que serve para o autor do post memorizar e aprender. Tendo sido fortemente inspirado em posts existentes publicamente na internet como este do site https://java-design-patterns.com/ (em Inglês).
Ver em outros idiomas: