Princípios de projetos
SOLID’os’
CI1163
Departamento de Informática
Universidade Federal do
Paraná
Curitiba – PR
SOLID
● Princípios de projeto orientado a objetos (mas que podem ser
generalizados)
○ Tem como objetivo auxiliar no desenvolvimento de software
■ Código “limpo”
■ Responsabilidades bem definidas
■ Facilidade de refatoração e manutenção
● Sub-conjunto de boas práticas
○ Não são ideias novas, mas permite categorização útil
● Não são GRASP’s, mas possui elementos relacionados
● Autor: Robert Martin (uncle Bob)
○ referência sobre o assunto: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
SOLID
● S ingle Responsibility Principle
● Open-Closed Principle
● L iskov Substitution Principle
● I nterface Segregation Principle
● Dependency Inversion Principle
Single Responsibility Principle (SRP)
● Uma classe deve ter UM, e somente um, MOTIVO para mudar
● Alterações causam “incerteza”
○ Cada linha modificada pode introduzir BUG novo
○ Diminui a coesão e aumenta acoplamento
● Mais restrito que o padrão especialista (cf. Larman)
● É o padrão “base”
● Anti-padrões: Classe Deus/Grande bola de lama/etc.
Este código segue o SRP ?
class Turma {
float calculaMedia(){/*...*/} Pontos chave:
List getAlunos(){/*...*/}
int getTotalAlunos(){/*...*/} Os dados são relativos às turmas,
aluno add(Aluno aluno){/*...*/} mas são separados em subconjuntos
void delete(Aluno aluno){/*...*/} de funcionalidades.
void imprimeDados(){/*...*/} Na manutenção, as modificações são
void mostraDados(){/*...*/} localizadas.
void load(){/*...*/}
void save(){/*...*/}
void update(){/*...*/}
void delete(){/*...*/}
}
Adaptando para o SRP
class Turma {
float calculaMedia(){/*...*/} Solução:
List getAlunos(){/*...*/}
int getTotalAlunos(){/*...*/} - separação em classes diferentes
aluno add(Aluno aluno){/*...*/}
void delete(Aluno aluno){/*...*/} - válido para outros elementos de
} software: métodos, atributos, pacotes,
class TurmaViewer { etc.
void imprimeDados(){/*...*/}
void mostraDados(){/*...*/}
}
class Repositorio {
void load(){/*...*/}
void save(){/*...*/}
void update(){/*...*/}
void delete(){/*...*/}
}
Open-Closed Principle (OCP)
● Objetos e/ou classes devem estar abertos para extensão, mas
fechados para modificação
○ É possível incluir novas funcionalidades
● Alterações causam “incerteza” (de novo)
● Abstração é a chave
● Utilização de bom encapsulamento
○ Atributos sempre privados
○ Uso de polimorfismo: criação de interfaces/classes abstratas
○ Jamais usar variáveis globais ou “similares”
● Anti-padrões: código espaguete
Este código segue o OCP ?
class Contrato {
float salario() {} Ponto chave:
}
class Estagio { Há um problema de acoplamento de
float bolsa() {} controle.
}
class Folha { Difícil de alterar a classe Folha.
protected int saldo;
public void calcular(){
if ( f instanceof Contrato )
this.saldo = f.salario();
else
if (f instanceof Estagio)
this.saldo = f.bolsa();
}
}
Adaptando para o OCP
interface Pagamento {
public float getSaldo(); Solução:
}
class Contrato implements Pagamento { - separação em classes diferentes
float getSaldo() {...} usando polimorfismo: objeto passado
} como parâmetro
class Estagio implements Pagamento {
float getSaldo() {...} - privilegiar atributos privados
}
class Folha { - NUNCA ter atributos estáticos
protected int saldo; “globais”
public float calcular(Pagamento p){
saldo = getSaldo();
}
}
Liskov* Substitution Principle(LSP)
● Uma classe derivada deve ser substituível pela sua classe base
○ “Se para cada objeto o1 do tipo S há um objeto o2 do tipo T de forma que, para todos os
programas P definidos em termos de T, o comportamento de P é inalterado quando o1 é
substituído por o2 então S é um subtipo de T
● Utilização de poliformismo adequado
○ A validade do modelo depende de seus filhos
○ Relacionamento IS-A ligado ao comportamento
■ Problemas em CASTs
● Pode ser relacionado com “Design por contrato” (Bertrand Meyer)
○ Pré condições e pós condições na execução
*Barbara Liskov - Turing Award em 2008, trabalhos sobre TAD
Este código segue o LSP ?
class Retangulo {
private int base, altura; Ponto chave:
void calculaArea(){...}
} Há no relacionamento IS-A - É-UM. O
class Quadrado extends Retangulo { quadrado não é um retângulo.
void calculaArea(){...}
} O que acontece com a base E altura ?
Podem impactar no resultado
if it looks like a duck, quacks like a duck
but needs batteries for that purpose - it’s
probably a violation of LSP
Adaptando para o LSP
class FormaGeometrica extends FormaGeometrica {
abstract void calculaArea(); Solução:
}
- criar classe abstrata e não ter
class Retangulo extends FormaGeometrica { novos métodos nas sub-classes →
private int base, altura; próximo à criação de interfaces
void calculaArea(){...}
} - ou realizar uma modelagem
class Quadrado extends FormaGeometrica { diferente, pode haver problema
private int lado; conceitual
void calculaArea(){...}
}
Interface Segregation Principle (ISP)
● Classe não implementa interface com métodos que não vai usar
● Evitar poluição da interface → baixo acoplamento
● Anti-padrão: interface “Deus”/martelo de ouro
Este código segue o ISP ?
interface Persistencia {
void save(); Ponto chave:
void insert(Object o);
void delete(); Os métodos são específicos a seus
void next(); tipos, não tentar fazer interfaces
void previous(); genéricas demais.
void append();
void write();
void read();
}
class BancoDados implements Persistencia{
}
class Arquivo implements Persistencia{
}
Adaptando para o ISP
interface PersistenciaBd {
void save(); Solução:
void insert(Object o);
void delete(); - a interface define apenas um
} conjunto de operações coesas → SRP
interface PersistenciaAquivo{
void append();
void write();
void read();
void next();
void previous();
}
class BancoDados implements PersistenciaBD{
}
class Arquivo implements PersitenciaArquivo{
}
Dependency Inversion Principle (DIP)
● Módulos de alto nível não devem depender de módulos de baixo nível.
Ambos devem depender de abstrações
● Abstrações não devem depender de detalhes. Detalhes (implementações)
devem depender de abstrações
● Utilização constante de polimorfismo
○ Facilita a reutilização
● Anti-padrão: complexidade “acidental”/gambiarra/copiar-colar
Este código segue o DIP ?
class LembraSenha { Ponto chave:
private Connection dbConn;
Há um problema de forte
LembraSenha() { acoplamento
dbConnection = new MySQLConnection();
} Instanciação de módulos de baixo
nível no lugar errado
}
Adaptando para o DIP
class LembraSenha {
private Connection dbConn; Solução:
LembraSenha(Connection c) { - Implementação baseada em
dbConnection = c; interface
}
- Identificar qual módulo é alto ou
} baixo nível
// definir a interface Connection e - Passagem dos objetos com a
// implementar diferentes subclasses implementação como parâmetros