Você está na página 1de 24

Padrões de Projeto

LIVRO 03
Padrões SOLID
Sumário
1. Padrões SOLID
2. Básico
S Single Responsability Principle (Princípio da responsabilidade única)
3. Intermediário
O Open­closed Principle (Princípio Aberto­fechado)
L Liskov Substitution Principle (Princípio da Substituição de Liskov)
4. Avançado
I Interface Segregation Principle (Princípio da Segregação da Interface)
D Dependency Inversion Principle (Princípio da inversão da Dependência)
5. Referências
Padrões SOLID
O SOLID é uma junção de princípios e boas práticas que visam melhorar um projeto, facilitando
sua manutenção e compreensão.
O termo SOLID surgiu após seu criador, Michael Feathers, observar que era possível unificar os
cinco princípios da orientação a objeto na sigla SOLID.
Esses princípios ajudam os desenvolvedores a melhorarem seu código, separando
responsabilidades, tornando o código mais limpo e consequentemente mais legível, facilitando a
manutenção, o reaproveitamento de trechos de códigos entre muitos outros benefícios.
Os padrões SOLID se baseiam nos quatro principais pilares da programação POO:
Abstração
Encapsulamento
Polimorfismo
Herança

1 2 3 4 5 6 7 8 9 10
S Responsabilidade Única Caso de Uso
Caso de Uso
Temos um projeto aonde uma classe chamada CozinheiroFazTudo possui três métodos para manipular a classe Prato:
fazerPrato, valorDoPrato e recolherPratos.

Esta não é a situação ideal, portanto vamos implementar o padrão S, de Responsabilidade Única.

Neste padrão, uma classe deve ter um, e somente um, motivo para atuar. Esse princípio declara que uma classe deve
ser especializada em um único assunto e possuir apenas uma responsabilidade dentro do software, ou seja, a classe
deve ter uma única tarefa ou ação para executar.

Para implementar o padrão vamos:

­ criar uma classe Cozinheiro, que vai apenas fazerPrato.


­ criar uma classe Caixa, que vai apenas cuidar do valorDoPrato.
­ criar uma classe Garçom, que vai apenas recolherPratos.

1 2 3 4 5 6 7 8 9 10
Diagrama
S Responsabilidade Única Conceitual

antes depois

Cozinheiro Cozinheiro Caixa Garçom


nome nome nome nome

faz
cobra faz cobra recolhe
recolhe
Prato Prato
nome nome
ingredientes ingredientes
preço preço

1 2 3 4 5 6 7 8 9 10
S Responsabilidade Única Implementação
Passo 1: implemente a classe Prato Passo 2: implemente a classe CozinheiroFazTudo
Esta classe será manipulada pelas outras classes. Esta classe vai ser responsável por todas as
operações sobre a classe Prato.

package srp;
package srp;
import java.util.List;
public class CozinheiroFazTudo {
public class Prato { String nome;
private String nome;
private List<String> ingredientes; public CozinheiroFazTudo(String nome) {
private double valor; System.out.println("========Cozinheiro que faz tudo - " + nome + "========");
this.nome = nome;
public Prato(String nome, List<String> ingredientes, double valor) { }
this.nome = nome;
this.ingredientes = ingredientes; public void fazerPrato(Prato prato) {
this.valor = valor; System.out.println("Fazendo prato " + prato.getNome());
} }
public String getNome() { return nome; } public double valorDoPrato(Prato prato) {
public List<String> getIngredientes() { return ingredientes; } return prato.getValor();
public double getValor() { return valor; } }
public void setNome(String nome) { this.nome = nome; } public void recolherPratos() {
public void setIngredientes(List<String> ingredientes){this.ingredientes = ingredientes; } System.out.println("Recolhendo pratos...");
public void setValor(double valor) { this.valor = valor; } }
} }

1 2 3 4 5 6 7 8 9 10
S Responsabilidade Única Implementação
Passo 3: implemente a classe Cozinheiro Passo 4: implemente a classe Caixa
Esta classe vai ser responsável apenas por fazer o Esta classe vai ser responsável apenas por calcular
Prato. o valor do Prato.

package srp; package srp;


public class Cozinheiro { public class Caixa {
String nome; String nome;
public Cozinheiro(String nome) { public Caixa(String nome) {
System.out.println("\n=======Cozinheiro especializado em fazer prato - " + nome + System.out.println("\n=======Caiixa especializado em calcular valor do prato - " +
"======="); nome + "=======");
this.nome = nome; this.nome = nome;
} }
public void fazerPrato(Prato prato) { public double valorDoPrato(Prato prato) {
System.out.println("Fazendo prato " + prato.getNome()); return prato.getValor();
} }
} }

1 2 3 4 5 6 7 8 9 10
S Responsabilidade Única Implementação
Passo 5: implemente a classe Garçom Passo 6: implemente a classe TesteErrado
Esta classe vai ser responsável apenas por rescolher Esta classe vai executar a situação antes da
os Pratos. implementação do padrão.

package srp;
package srp; import java.util.ArrayList;
public class Garçom { import java.util.List;
String nome; public class TesteErrado {
public Garçom(String nome) { public static void main(String[] args) {
System.out.println("\n========Cozinheiro especializado em recolher prato - " + List<String> ingredientesPrato1 = new ArrayList<String>();
nome + "========"); ingredientesPrato1 .add("Costela de porco");
this.nome = nome; ingredientesPrato1 .add("Linguiça");
} ingredientesPrato1 .add("Bacon");
public void recolherPratos() { ingredientesPrato1 .add("Feijão preto");;
System.out.println("Recolhendo pratos..."); Prato p1 = new Prato("Feijoada", ingredientesPrato1 , 29.99);
}
}
CozinheiroFazTudo cozinheiro = new CozinheiroFazTudo("Roberto");
cozinheiro.fazerPrato(p1 );
System.out.println("Valor do prato: " + cozinheiro.valorDoPrato(p1 ));
cozinheiro.recolherPratos();
}
}

1 2 3 4 5 6 7 8 9 10
S Responsabilidade Única Implementação
Passo 7: implemente a classe TesteCerto
package srp;
Esta classe vai executar a situação depois da import java.util.ArrayList;
implementação do padrão. public class TesteCerto {
Neste padrão, cada classe tem sua public static void main(String[] args) {
responsabilidade: o cozinheiro faz o prato, o caixa ArrayList<String> ingredientesPrato1 = new ArrayList<String>();
ingredientesPrato1 .add("Costela de porco");
define o valor do prato, o garçom recolhe os pratos. ingredientesPrato1 .add("Linguiça");
ingredientesPrato1 .add("Bacon");
ingredientesPrato1 .add("Feijão preto");
ingredientesPrato1 .add("Cebola");
ingredientesPrato1 .add("Alho");
ingredientesPrato1 .add("Sal");;
Prato p1 = new Prato("Feijoada", ingredientesPrato1 , 29.99);
Cozinheiro cozinheiro = new Cozinheiro("Felipe");
cozinheiro.fazerPrato(p1 );
Caixa caixa = new Caixa("Casemiro");
System.out.println("Valor do prato: " + caixa.valorDoPrato(p1 ));
Garçom garçom = new Garçom("Gustavo");
garçom.recolherPratos();
}
}

1 2 3 4 5 6 7 8 9 10
O Aberto­fechado Caso de Uso e
Diagrama Conceitual

Diagrama Conceitual
Caso de Uso
A classe Cubo está pronta, tem seus atributos e
métodos definidos. Aplicação

volumeTotal
Precisamos implementar o calculo do volume do
cubo.

Seguindo o padrão aberto­fechado, o cubo está


fechado por modificações, porém aberto para Calcular
extensões. Cubo
comprimento
largura
Vamos então criar uma aplicação específica (uma altura
extensão) para fazer o cálculo.

1 2 3 4 5 6 7 8 9 10
O Aberto­fechado Implementação
Passo 1: implemente a classe Cubo Passo 2: implemente a classe Aplicação
Esta classe define o cubo que não será alterada no Esta classe vai extender a funcionalidade do cubo,
futuro, vamos criar uma aplicação. sem altera­lo

package exemploocp; package exemploocp;


/* /*
Classe auxiliar, Ela contém as dimensões do cubo Classe auxiliar, Realiza o cálculo do volume do cubo
*/ */
public class Cubo { public class Aplicacao {
//Declaração das variáveis //retorna o volume total de todos os cubos que se encontram dentro do array
public double comprimento; public double volumeTotal(Cubo[] objetosCubo) {
public double largura; // Variavel que armazena a soma do volume
public double altura; double volumeSoma = 0;
public double getComprimento() {return comprimento; } //Entra no for que vai "andar" pelo array
public double getLargura() {return largura; } for (Cubo i : objetosCubo) {
public double getAltura() {return altura; }
//realiza a fórmula para encontrar o volume e soma com o total do volume atual
public void setComprimento(double comprimento) {this.comprimento = comprimento; } volumeSoma = i.comprimento * i.largura * i.altura + volumeSoma;
public void setLargura(double largura) {this.largura = largura; } }
public void setAltura(double altura) {this.altura = altura; }
} // returning the to total volume
return volumeSoma;
}
}

1 2 3 4 5 6 7 8 9 10
O Aberto­fechado Implementação
Passo 3: implemente a classe que executa
//Declaracao de uma array que vai receber os dados dos cubos
Cubo[] arrayCubo = new Cubo[3];
Esta classe vai executar a aplicação, que vai arrayCubo[0] = cb1 ;
extender a funcionalidade do cubo. arrayCubo[1 ] = cb2;
arrayCubo[2] = cb3;
//Inicializo a classe aplicacao
package exemploocp; Aplicacao app = new Aplicacao();
/* //Envia para a classe aplicacao o arrayCubo e recebe o volume total
Classe principal , Nela vai conter a declaracao do cubo e a insercao de dados double volume = app.volumeTotal(arrayCubo);
*/
// Print and Display the Total Volume
public class ExemploOCP { System.out.println("O volume total de todos os cubos eh " + volume);
public static void main(String[] args) {
}
//inicializando os cubos }
Cubo cb1 = new Cubo();
Cubo cb2 = new Cubo();
Cubo cb3 = new Cubo();
//inserindo os dados dos cubos
cb1 .comprimento = 5; cb1 .largura = 1 0; cb1 .altura = 1 5;
cb2.comprimento = 2; cb2.largura = 4; cb2.altura = 6;
cb3.comprimento = 3; cb3.largura = 1 2; cb3.altura = 1 5;

1 2 3 4 5 6 7 8 9 10
L Substituição de Liskov Caso de Uso

Caso de Uso
O Princípio de Substituição de Liskov afirma que os objetos de uma superclasse devem ser substituídos por objetos de
suas subclasses sem quebrar o aplicativo.

Em outras palavras, o que queremos é que os objetos de nossas subclasses se comportem da mesma forma que os
objetos de nossa superclasse.

1 2 3 4 5 6 7 8 9 10
L Substituição de Liskov Diagrama Conceitual
A ser implementado pelos alunos.

1 2 3 4 5 6 7 8 9 10
L Substituição de Liskov Implementação

A ser implementado pelos alunos.

1 2 3 4 5 6 7 8 9 10
I Segregação da Interface Caso de Uso

Esse padrão de projetos diz que cada classe deve possuir uma única responsabilidade. Desse modo o código torna­
se mais claro e reutilizável. A desvantagem é que teremos um número de classes maior que teríamos sem o padrão. A
alta coesão significa que cada classe terá suas responsabilidades altamente focadas.

Neste caso, temos uma interface que implementar as ações de nadar, correr e arremessar. a classe Atleta contrata a
interface AtletaAcoes, e isso vai obrigar a classe Atleta a implementar todas as três ações.

Seguindo o padrão de segregação da interface, vamos criar três interfaces distintas, uma para cada ação. Isso
permitirá que o classe atleta contrate apenas a interface que lhe interessa.

1 2 3 4 5 6 7 8 9 10
I Segregação da Interface Diagrama
Conceitual

antes depois

<<interface>> <<interface>> <<interface>> <<interface>>


AtletaAcoes AtletaNatacao AtletaArremesso AtletaCorrida

nadar nadar arremessar correr


correr
arremessar

Atleta Atleta

1 2 3 4 5 6 7 8 9 10
I Segregação da Interface Implementação

Passo 1: implemente a interface AtletaAcoes Passo 2: implemente a classe Atleta


Esta interface contem todas os métodos, ainda sem Esta classe contrata a interface AtletaAcoes, o que
a implementação do padrão. obriga a classe Atleta a implementar todas as ações.

package NoISPExample; package NoISPExample;


public interface AtletaAcoes { public class Atleta implements AtletaAcoes{
void nadar();
@Override
void correr(); public void nadar() {
throw new UnsupportedOperationException("Atleta não necessita nadar");
void arremessar(); }
}
@Override
public void correr() {
throw new UnsupportedOperationException("Atleta não necessita correr.");
}
@Override
public void arremessar() {
System.out.println("Atleta está arremessando ...");
}
}

1 2 3 4 5 6 7 8 9 10
I Segregação da Interface Implementação

Passo 3: implemente as interfaces separadas Passo 4: implemente a classe Atleta novamente


Seguindo o padrão de segregaão da interface, Esta classe contrata a interface AtletaArremesso, já
implemente as três interfaces separadas. que ela vai implementar apenas esta ação.

package ISPExample; package ISPExample;


public interface AtletaArremesso { public class Atleta implements AtletaArremesso{
void arremessar();
} @Override
public void arremessar() {
System.out.println("Atleta está arremessando ...");
}
package ISPExample; }
public interface AtletaCorrida {
void correr();
}

package ISPExample;
public interface AtletaNatacao {
void nadar();
}

1 2 3 4 5 6 7 8 9 10
D Inversão da dependência Caso de Uso e
Diagrama Conceitual

Caso de Uso
As classes devem depender de abstrações e não de coisas concretas.

A ideia é: sempre que uma classe for depender de outra, ela deve depender sempre de outro módulo mais estável do
que ela mesma. Se A depende de B, a ideia é que B seja mais estável que A. Mas B depende de C. Logo, a ideia é
que C seja mais estável que B.

Ou seja, suas classes devem sempre andar em direção à estabilidade, depender de módulos mais estáveis que ela
própria.

Mas como conseguir isso? Lembre­se que abstrações tendem a ser estáveis, e implementações instáveis. Se você está
programando alguma classe qualquer com regras de negócio, e precisa depender de outro módulo, idealmente
esse outro módulo deve ser uma abstração. Tente ao máximo não depender de outras implementações (afinal, elas
são instáveis).

1 2 3 4 5 6 7 8 9 10
D Inversão da dependência Implementação

A ser implementado pelos alunos.

1 2 3 4 5 6 7 8 9 10
Referências
Orientação a Objetos e SOLID para Ninjas ­ Maurício Aniche
Pesquisa Single Responsability ­ Daniel Borba Guimães da Costa / Thais Resende Araújo Borges
Bonfim
Pesquisa Open­closed, Interface Segregation ­ David Gabriel B. Jorge / Murillo Sampaio
Oliveira Antonio

Krita Inkscape Scribus Pexels Gimp Synfig Audacity Kdenlive

1 2 3 4 5 6 7 8 9 10
Só mais uma coisa...

@laertecoutinho1

O professor não é dono do saber.


O professor é um organizador de conhecimento, que tem a experiência adequada
para selecionar o que é relevante neste mar infinito de informação.
marco@iftm.edu.br

@marcomacielpro Produzido em 2022

Você também pode gostar