Looks like no one added any tags here yet for you.
Uma classe deve ter apenas uma única responsabilidade ou motivo para mudar, o que aumenta a coesão e facilita a manutenção do código
// Classe responsável apenas pelo cálculo do salário
public class CalculadoraSalario {
public double calcularSalario(double horas, double taxa) {
return horas * taxa;
}
}
// Classe responsável apenas pela geração de relatórios
public class RelatorioSalario {
public void gerarRelatorio(double salario) {
System.out.println("Salário: " + salario);
}
}
SOLID
Princípio da Responsabilidade Única (SRP)
O que é o SRP e por que ele é importante?
Classes devem ser abertas para extensão, mas fechadas para modificação. Use herança ou composição para estender funcionalidades sem alterar o código existente.
public abstract class Desconto {
public abstract double aplicar(double valor);
}
public class DescontoNatal extends Desconto {
public double aplicar(double valor) {
return valor * 0.9; // 10% de desconto
}
}
public class Pedido {
private Desconto desconto;
public Pedido(Desconto desconto) {
this.desconto = desconto;
}
public double calcularTotal(double valor) {
return desconto.aplicar(valor);
}
}
SOLID
Princípio Aberto/Fechado (OCP)
Como aplicar o Princípio Aberto/Fechado?
Objetos de classes derivadas devem ser substituíveis pelos objetos da classe base sem alterar o comportamento esperado.
// Classe base
public class Animal {
public void emitirSom() {
System.out.println("Som genérico de animal");
}
}
// Subclasse que substitui corretamente a classe Animal
public class Cachorro extends Animal {
@Override
public void emitirSom() {
System.out.println("Au Au");
}
}
// Outra subclasse que substitui corretamente a classe Animal
public class Gato extends Animal {
@Override
public void emitirSom() {
System.out.println("Miau");
}
}
// Classe de teste demonstrando a substituição
public class TesteAnimais {
public static void main(String[] args) {
Animal animal1 = new Cachorro();
Animal animal2 = new Gato();
// Mesmo tipo base, mas comportamento específico de cada subclasse
animal1.emitirSom(); // Imprime "Au Au"
animal2.emitirSom(); // Imprime "Miau"
}
}
Neste exemplo, tanto Cachorro
quanto Gato
são subclasses de Animal
e substituem o método emitirSom()
sem alterar a expectativa do comportamento da classe base. Assim, podemos usar instâncias dessas subclasses onde quer que um Animal
seja esperado, cumprindo o LSP.
SOLID
Princípio da Substituição de Liskov (LSP)
Qual a importância do Princípio da Substituição de Liskov?
Prefira interfaces específicas em vez de uma interface “gorda”. Uma classe não deve ser forçada a implementar métodos que não utiliza.
public interface Impressora {
void imprimir();
}
public interface Digitalizadora {
void digitalizar();
}
public class MultiFuncional implements Impressora, Digitalizadora {
public void imprimir() {
System.out.println("Imprimindo...");
}
public void digitalizar() {
System.out.println("Digitalizando...");
}
}
SOLID
Princípio da Segregação de Interfaces (ISP)
O que diz o Princípio da Segregação de Interfaces?
Dependa de abstrações (interfaces) e não de implementações concretas, facilitando a manutenção e testes.
public interface Logger {
void log(String mensagem);
}
public class ConsoleLogger implements Logger {
public void log(String mensagem) {
System.out.println(mensagem);
}
}
public class Processador {
private Logger logger;
public Processador(Logger logger) {
this.logger = logger;
}
public void processar() {
logger.log("Processamento iniciado.");
}
}
SOLID
Princípio da Inversão de Dependência (DIP)
Como a Inversão de Dependência melhora o design do código?
Garantir que uma classe tenha apenas uma instância e fornecer um ponto global de acesso a ela.
public class Singleton {
private static Singleton instancia;
private Singleton() { }
public static Singleton getInstancia() {
if (instancia == null) {
instancia = new Singleton();
}
return instancia;
}
}
Design Patterns
Singleton
Qual o objetivo do padrão Singleton?
Define uma interface para criar objetos, permitindo que subclasses decidam qual classe instanciar, promovendo flexibilidade e desacoplamento
public interface Animal {
void fazerSom();
}
public class Cachorro implements Animal {
public void fazerSom() {
System.out.println("Au Au");
}
}
public class Gato implements Animal {
public void fazerSom() {
System.out.println("Miau");
}
}
public class AnimalFactory {
public static Animal criarAnimal(String tipo) {
if (tipo.equals("cachorro")) {
return new Cachorro();
} else if (tipo.equals("gato")) {
return new Gato();
}
throw new IllegalArgumentException("Tipo desconhecido");
}
}
Design Patterns
Factory Method
O que é o Factory Method e quando usá-lo?
Permite definir uma família de algoritmos, encapsular cada um e torná-los intercambiáveis. Isso possibilita alterar o comportamento de um objeto em tempo de execução.
public interface Comportamento {
void mover();
}
public class ComportamentoNormal implements Comportamento {
public void mover() {
System.out.println("Movendo normalmente...");
}
}
public class ComportamentoAgressivo implements Comportamento {
public void mover() {
System.out.println("Movendo agressivamente...");
}
}
public class Robo {
private Comportamento comportamento;
public Robo(Comportamento comportamento) {
this.comportamento = comportamento;
}
public void setComportamento(Comportamento comportamento) {
this.comportamento = comportamento;
}
public void mover() {
comportamento.mover();
}
}
Design Patterns
Strategy
Como o padrão Strategy pode ser útil no design de um software?
Permite que um objeto (sujeito) notifique automaticamente um conjunto de objetos (observadores) sobre alterações em seu estado.
import java.util.ArrayList;
import java.util.List;
public interface Observer {
void atualizar(String mensagem);
}
public class Assinante implements Observer {
public void atualizar(String mensagem) {
System.out.println("Notificado: " + mensagem);
}
}
public class Publicador {
private List<Observer> observers = new ArrayList<>();
public void adicionarObserver(Observer observer) {
observers.add(observer);
}
public void notificarObservers(String mensagem) {
for (Observer o : observers) {
o.atualizar(mensagem);
}
}
}
Design Patterns
Observer
Qual a finalidade do padrão Observer?
Permite adicionar responsabilidades a um objeto de forma dinâmica, sem alterar sua estrutura original.
public interface Cafe {
double custo();
}
public class CafeSimples implements Cafe {
public double custo() {
return 5.0;
}
}
public class CafeComLeite implements Cafe {
private Cafe cafe;
public CafeComLeite(Cafe cafe) {
this.cafe = cafe;
}
public double custo() {
return cafe.custo() + 2.0;
}
}
Design Patterns
Decorator
Como o padrão Decorator auxilia na extensão de funcionalidades?
SOLID é um conjunto de cinco princípios de design de software que ajudam a criar sistemas mais robustos, flexíveis e de fácil manutenção. Esses princípios servem como diretrizes para estruturar o código de forma que seja fácil de entender, testar e evoluir.
Aplicar os princípios SOLID resulta em um código mais limpo, organizado e resiliente a mudanças, facilitando tanto o desenvolvimento quanto a manutenção a longo prazo. Esses princípios são particularmente úteis em projetos de software que precisam de flexibilidade e escalabilidade, independentemente de seu tamanho inicial.
SOLID
O que é SOLID?
Manutenção: Facilita a alteração e evolução do código sem grandes impactos em outras partes do sistema.
Legibilidade: Código bem estruturado torna mais fácil a compreensão por outros desenvolvedores.
Reutilização: Promove a criação de componentes independentes e reutilizáveis.
Testabilidade: Ajuda a criar códigos com baixo acoplamento, tornando a realização de testes unitários mais eficiente.
Escalabilidade: Sistemas construídos com SOLID são mais adaptáveis a mudanças de requisitos e novas funcionalidades.
SOLID
Por que usar SOLID?
SRP (Responsabilidade Única): Cada classe deve ter apenas uma responsabilidade, ou seja, apenas uma razão para mudar.
OCP (Aberto/Fechado): Estruture suas classes para que possam ser estendidas sem a necessidade de modificar o código existente.
LSP (Substituição de Liskov): Garanta que subclasses possam substituir suas classes base sem alterar o comportamento esperado.
ISP (Segregação de Interfaces): Divida interfaces grandes em menores e mais específicas, permitindo que as classes implementem apenas os métodos que realmente utilizam.
DIP (Inversão de Dependência): Dependa de abstrações (interfaces ou classes abstratas) em vez de implementações concretas, facilitando a troca de componentes.
SOLID
Como usar SOLID?
Durante a Arquitetura do Projeto: Ao iniciar um novo projeto, aplicar SOLID desde o início ajuda a evitar dívidas técnicas e problemas de manutenção.
Em Projetos de Crescimento: Em sistemas que devem evoluir e receber novas funcionalidades ao longo do tempo, SOLID facilita a expansão sem comprometer a estrutura existente.
Ao Refatorar Código Legado: Mesmo em projetos existentes, a aplicação gradual dos princípios SOLID pode melhorar a qualidade e a manutenibilidade do código.
Desenvolvimento Colaborativo: Em ambientes com equipes de desenvolvimento, ter um código organizado e padronizado melhora a comunicação e a integração entre os desenvolvedores.
SOLID
Quando usar SOLID?
Design Patterns
O que são Design Patterns?
Design Patterns são soluções reutilizáveis para problemas comuns de design de software. Eles representam “receitas” que ajudam a estruturar e organizar o código de forma mais eficiente e robusta, permitindo a criação de sistemas escaláveis e de fácil manutenção.
Design Patterns
Por que usar Design Patterns?
Reutilização: Aproveitam soluções já testadas e comprovadas, evitando a reinvenção da roda.
Manutenção: Facilitam a manutenção e evolução do sistema, pois promovem um design modular e de baixo acoplamento.
Comunicação: Fornecem uma linguagem comum entre os desenvolvedores, agilizando discussões e implementações.
Flexibilidade: Permitem a criação de sistemas mais adaptáveis a mudanças, melhorando a escalabilidade do software.
Design Patterns
Como usar Design Patterns?
Estudo e Conhecimento: Familiarize-se com os padrões mais comuns (por exemplo, Singleton, Factory, Observer, Strategy, Decorator, etc.) e entenda em quais contextos cada um é aplicado.
Identificação do Problema: Analise o design do seu sistema para identificar problemas recorrentes que podem ser resolvidos por meio de um padrão.
Adaptação ao Contexto: Selecione o padrão que melhor se encaixa na situação e adapte-o conforme as necessidades específicas do seu projeto.
Documentação e Comunicação: Documente a implementação do padrão para facilitar a compreensão e manutenção futura, garantindo que toda a equipe esteja alinhada.
Problemas Recorrentes: Ao enfrentar desafios que se repetem em diferentes partes do sistema, como a criação controlada de objetos ou a necessidade de notificação de mudanças de estado.
Sistemas em Crescimento: Em projetos que precisam evoluir constantemente, onde a flexibilidade e a escalabilidade do design são essenciais.
Trabalho em Equipe: Para facilitar a comunicação e o entendimento entre desenvolvedores, especialmente em equipes grandes ou distribuídas.
Manutenção a Longo Prazo: Em projetos que demandam manutenção e evolução contínua, onde um design modular e bem estruturado reduz custos e riscos.
Design Patterns
Quando usar Design Patterns?