Seja um Desenvolvedor, não um liquidificador

Olá, tudo bem?

Vivemos em uma época onde é muito fácil de criar projetos com frameworks “mágicos”. O que é ensinado em diversos blogs e faculdades é que: “basta anotar com isso aqui que está pronto”.

Anotações tem a função de facilitar a utilização e a identificação de comportamentos dos frameworks, mas existem desenvolvedores que se perdem com essa facilidade e acabam esquecendo de conceitos básicos.

CRUD, filas JMS, Beans, controle de transações podem ser criados em minutos, mas e a parte de Orientação a Objeto? Conceitos básicos do Java? O que tem acontecido para que as boas práticas que demoraram anos para serem desenvolvidas simplesmente serem abandonadas?

Misturando Conceitos

Para ser mais claro, vamos ver alguns exemplos de códigos postados por desenvolvedores em fóruns. No primeiro exemplo é possível ver a mistura de JPA com JSON:

@Entity
@XmlRootElement
@JsonAutoDetect
@DiscriminatorColumn(name = "animal_type")
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "cat"),
        @JsonSubTypes.Type(value = Bird.class, name = "bird")
})
public class Animal {

    @Id
    private int id;

    @JsonSerialize(using = JsonDateSerializer.class)
    private Date birthday;

    @JsonView(ToothView.class)
    @OneToMany(mappedBy = "animal")
    private List<Tooth> teethList;

    // other stuff
}

@Entity
@JsonRootName(value = "Dog")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Dog extends Animal {

    @JsonProperty
    private String name;

    @Convert(converter = JsonAttributeConverter.class)
    private Double weight;

    @JsonIgnore
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "dog", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Bone> boneList;

    public Dog() {
    }
}

Qual o problema do código acima? Qual o motivo da mistura de anotações de JSON (view) com anotações do JPA (modelo)? Note que, por causa dessa mistura, qualquer campo que a view deva exibir de modo diferente afetará justamente a mesma classe utilizada para salvar dados no banco de dados.

Não respeitando as camadas

Vamos a outro exemplo:

@ViewScoped
@ManagedBean
@TransactionManagement(TransactionManagementType.BEAN)
public class ManagedBeanOne extends SuperManagedBean implements Serializable {
     // attributes
    public String doSomething() {
        try{
            transaction.begin(); 
            // do something
            transaction.commit();
        }
        catch(Exception ex){
            transaction.rollback();
        }
    }
}


@ManagedBean
@WebService
@ViewScoped
public class ManagedBeanTwo implements Serializable{}

Por que fazer de um ManagedBean de JSF também como um EJB? E até pior, por que deixar que um ManagedBean controle uma transação? Qual o ganho em deixar o ManagedBean com diversas responsabilidades?

Tamanho mais atrapalha que ajuda

Um exemplo que eu não tive como colocar aqui foi de uma classe com mais de 1k de linhas. A pior parte é saber que esse tipo de classe fica sempre como motivo de chacota, ou essa classe é tratada como um objeto de terror e espanto pois qualquer alteração quebra o sistema.

Até quando os desenvolvedores vão ter o hábito de sempre querer adicionar um if sem antes fazer um pequeno refactoring?

Exception que é Chefe

Outra prática terrível é deixar uma exceção controlar o fluxo. Veja como é fácil cometer esse erro:

try{
    myObject.doSomething();
} catch(MyExceptionOne one){
    sendErrorSmsToCustomer();
    defineOldPlanToCustomer();
    sentToScreenOne();
} catch(MyExceptionTwo two){
    sendErrorEmailToCustomer();
    defineNewPlanToCustomer();
    sentToScreenTwo();
}

No exemplo acima, o que aconteceria com os dados do banco se na primeira linha de uma exceção acontecesse um erro? O que aconteceria se algum desenvolvedor fosse executar a chamada myObject.doSomething(); em alguma outra classe, e não tratasse todos os erros como no exemplo acima? E se ele não adicionasse a linha onde não trata o plano do usuário?

E agora?

Falar dos problemas é fácil, mas eu creio que ser necessário mostrar soluções que podemos aplicar em nosso dia a dia para melhorar a qualidade de código. O que vamos ver nas próximas páginas não é nada inventado por mim, mas são práticas extraídas de livros sobre design patterns e boas práticas de programação.

O que veremos:

  • Página 2: Não transpasse camadas
    • Atenção nos pacotes
  • Página 3: Aumente a coesão
  • Página 4: Reduza o acoplamento
    • Facade é uma boa pedida
  • Página 5: Voltemos ao mundo dos DTOs
    • Como facilitar a cópia de uma Entity para um DTO
  • Página 6: Métodos e Classes pequenas
  • Página 7: Como usar exceção corretamente?
  • Página 8: Teoria da Janela quebrada
    • Mantenha seu build sempre funcionando
    • Nunca deixe testes quebrados
    • Não comente/ignore seus testes
  • Página 9: Boas práticas que ajudam
    • KISS
    • DRY
  • Página 10: Quantidade de testes não importa, mas sim a qualidade
    • Utilize ferramentas
    • Achou um bug? Crie um teste
  • Página 11: Conclusão

Espero que este post seja útil ao seu dia a dia. (:

Não poderia deixar de agradecer ao Rodrigo Sasaki (http://cv.rodrigosasaki.com/) que me deu uma força absurda na revisão do post.

Design Pattern – Observer (Parte 02)

Pessoal, bom dia.

Hoje vamos continuar falando sobre o padrão Observer. Seria muito bom que você lesse a primeira parte, caso não o tenha feito Design Pattern – Observer (Parte 01).

Continuando o último post (Design Pattern – Observer (Parte 01)), como poderíamos desacoplar nossas classes?

Um bom modo para criar esse desacoplamento de métodos, seria: a classe Listener passe a ter uma referência da classe Notifier. Bem simples “né”?

Desse modo, a nossa classe Notifier, ela irá apenas dizer ao seu Listener que houve alteração em seu status e o Listener irá buscar todos os parâmetros que necessita.

Continuando no exemplo, que a classe YahooPattern a partir de agora necessitará de uma senha para enviar o email, nossa classe consultaria na classe Notifier os dados que necessitasse. Vamos começar as alterações?

Primeiramente precisamos alterar o método “update”. Para isso, que tal aplicarmos outro modo de “programar para interface”? Vamos atualizar nosso modelo e utilizar uma superclasse abstrata.

Design Pattern - Observer (Parte 02)Design Pattern - Observer (Parte 02)

Alteramos nossa interface Notifier, ela contém agora métodos que serão visíveis aos nossos listeners, para que os listeners possam buscar as informações que necessitarem. As classes que representam o envio de email (EmailPattern) apresentam uma referência do tipo Notifier.

Essa referência foi criada para que o Listener possa buscar os dados que ele quiser e não a Notifier seja o responsável por conhecer a necessidade de cada Listener. Sendo assim, cada Listener pode alterar os parâmetros necessários que nosso método update continuará o mesmo, a alteração seria no método “collectData” que toda classe do tipo EmailPattern é obrigada a construir. Com isso, caso a classe GmailPattern precisasse de senha, apenas ela sofreria alteração e mais ninguém.

Se fosse um novo requisito que a classe HotmailPattern utilizasse certa imagem, bastaria adicionar em nossa classe Notifier um método “get” (como forma acesso), para que a classe HotmailPattern utilizar esse valor. Note que a classe Notifier está sofrendo uma alteração apenas para fornecer uma informação, ela não estará interessada em quem/como estará utilizando essa informação.

Vamos ver nosso código final:

public class EmailSender implements Notifier {

    private List listeners;
    private String emailText;
    private String emailSubject;
    private String password;

    public EmailSender() {
        listeners = new ArrayList();
    }

    public void addListener(Listener listener) {
        listeners.add(listener);
    }

    public void removeListener(Listener listener) {
        listeners.remove(listener);
    }

    public void createEmails() {
        emailText = getNewEmailTextFromDataBase();
        emailSubject = getNewEmailTitleFromDataBase();
        password = getPasswordFromDataBase();
        notifyListeners();
    }

    public void notifyListeners() {
        for (Listener listener : listeners) {
            listener.update();
        }
    }

    // Getters and Setters
}
// Our superclass EmailPattern. All Emails patterns will extend this class
public abstract class EmailPattern implements Listener{
    protected Notifier emailSender;

    public EmailPattern(Notifier emailSender){
        this.emailSender = emailSender;
    }

    public void update(){
        collectData();
        sendEmail();
    }

    protected abstract void collectData();
    protected abstract void sendEmail();
}
// This is our implementation. The class will get the password and any other data that it needs
public class YahooPattern extends EmailPattern {
    private String emailText;
    private String emailSubject;
    private String emailPassword;

    public YahooPattern(Notifier emailSender) {
        super(emailSender);
    }

    @Override
    protected void collectData() {
        emailText = emailSender.getEmailText();
        emailSubject = emailSender.getEmailSubject();
        emailPassword = emailSender.getPassword();
    }

    @Override
    public void sendEmail() {
        System.out.println("Sending email subject: " + emailSubject + " with the text: " + emailText + " with the password: " + emailPassword);
    }
}

Com isso, podemos chegar a mais um princípio de Design:
"Procure sempre diminuir o acoplamento em objetos que se comuniquem."
Nós realizamos esse desacoplamento ao simplesmente fazer com que nossa classe Notifier possa fornecer dados, mas não entender o que cada Listener necessita. Ela apenas coleta todos os dados e chama cada Listener, e o Listener por sua vez é quem sabe qual informação utilizar.

Um resumo do visto até hoje:
Padrão de Projeto: Strategy
Princípios vistos:

  • Identifique tudo o que varia e separe do restante que não irá variar.
  • Programe para interface.
  • Dar prioridade à composição.

Padrão de Projeto: Observer
Princípios vistos:

  • Procure sempre diminuir o acoplamento em objetos que se comuniquem.


Qualquer dúvida/colocação basta escrever.

Espero que você goste, até a próxima.

Revisado por: Renan Alves dos Santos

Design Pattern – Observer (Parte 01)

Olá pessoal, tudo bem?

Hoje vamos ver sobre o Padrão de Projeto “Observer”. Caso queira ver sobre o primeiro padrão, Strategy, clique aqui. Apenas para revisar vamos ver o pequeno quadro abaixo que exibe o que já foi estudado sobre o assunto de Design Pattern:
Padrão de Projeto: Strategy
Princípios vistos:

  • Identifique tudo o que varia e separe do restante que não irá variar.
  • Programe para interface.
  • Dar prioridade à composição.

Vamos imaginar o seguinte caso de uso:
A empresa mail4Usted de mala direta tem um sistema especializado em enviar emails. A empresa tem um sistema que aplica configurações específicas para cada tipo de servidor de email.
A mail4Usted solicita que seja efetuada uma alteração em seu sistema. O problema é que a antiga empresa que dava manutenção em seu sistema sumiu do mapa, então o máximo que a mail4Usted poderá fazer é fornecer o código fonte antigo.

O sistema atualmente envia email apenas para clientes que tenham email no Yahoo, e Gmail. Foi solicitado pela empresa que seja adicionado primeiramente envio de email para o Hotmail, se a mail4Usted gostar do trabalho realizado fechará contrato para adicionar outros provedores de email.

Nosso mais novo cliente também explica que essa divisão de email é necessária, pois uma formatação especial para cada servidor de email é aplicada.”

O código que nos foi deixado é o seguinte:

public class EmailSender{
    public void sendNewEmails(){
        String emailText = getNewEmailTextFromDataBase();
        String emailSubject = getNewEmailTitleFromDataBase();

        sendEmailToGmail(emailText, emailSubject);
        sendEmailToYahoo(emailText, emailSubject);
    }

    //Others methods
}

Em uma solução pobre, bastaria implementar o método para enviar email ao Hotmail e estaríamos felizes:

public class EmailSender{
    public void sendNewEmails(){
        String emailText = getNewEmailTextFromDataBase();
        String emailSubject = getNewEmailTitleFromDataBase();

        sendEmailToGmail(emailText, emailSubject);
        sendEmailToYahoo(emailText, emailSubject);

        // Our new method
        sendEmailToHotmail(emailText, emailSubject);
    }

    //Others methods
}

Mas imagine se toda vez que um servidor de email tiver que ser adicionado ou sofrer manutenção, nossa classe principal será alterada. Ficará uma classe longa, de difícil manutenção e não muito coesa.

Para melhorar poderemos aplicar dois padrões de projeto: a começar pelo Strategy e depois Observer.

O que varia em nosso código que poderíamos isolar? Note que temos um comportamento que sempre será diferente e toda alteração que acontecer afetará nossa classe principal, algo que não precisa acontecer.

Aplicando o princípio visto no Strategy iremos separar esse comportamento. Mas para deixar ainda mais interessante, que tal entender um pouco sobre o padrão Observer para aplicar os dois juntos? Vamos ver um pequeno exemplo para nos dar uma idéia sobre como padrão Observer funciona:
Imagine que você se cadastra em uma loja para receber anúncios de promoções sobre seus produtos. A partir de então mesmo que você não tenha a mínima idéia de quando acontecerá uma promoção, você irá receber essa notificação.
Perceba que você será alvo de uma notificação de promoção mesmo estando totalmente ocupado com outras atividades. Talvez você esteja lendo um jornal, vendo TV, passeando com o cachorro e mesmo assim receberá essa notificação.

Por que não aplicarmos essa idéia aqui? Poderíamos fazer com que cada comportamento de enviar email fosse isolado (Strategy). Depois que o comportamento for isolado em uma classe, essa classe irá receber uma notificação informando que ela já tem os dados solicitados (funcionando como um bom Listener). Veja que será aplicado o padrão Observer que funciona assim: a ClasseA informa à ClasseB uma informação necessária e a ClasseB irá trabalhar com essa informação.

Poderíamos definir o padrão Observer por:
Define uma relação de dependência “Um para Muitos”. Quando o estado desse objeto mudar, todos os outros relacionados a ele serão notificados dessa mudança.
Em nosso caso utilizaremos a classe do tipo EmailSender para notificar a cada classe do tipo EmailHost que acontecerá uma nova rotina para envio de email.

Primeiro vamos ver como ficarão nossas interfaces (lembre-se de sempre programar para interfaces, sendo que interfaces significam um super-tipo!):Design Pattern Observer Parte 01Teremos a partir de agora um código que será mais flexível a mudanças, com bons padrões começando a ser aplicados.

O Listener será notificado de que alguma mudança aconteceu (método update(…)) no estado de nosso “Notifier” (que é o responsável por notificar). Após perceber essa mudança, nosso listener irá enviar o email (método sendEmail()).

Nossas classes do tipo Listener e EmailPattern:Design Pattern Observer Parte 01

Nossa classe do tipo Notifier:Design Pattern Observer Parte 01
Note que isolamos vários comportamentos que variam (Strategy). Isolamos “quem” irá enviar email (classes EmailPattern) e “quem” irá preparar e notificar essas classes (EmailSender).

Veja agora como ficarão as nossas classes implementadas:

public class EmailSender implements Notifier {
    private List listeners;
    private String emailText;
    private String emailSubject;

    public EmailSender(){
        listeners = new ArrayList();
    }

    public void addListener(Listener listener){
        listeners.add(listener)
    }

    public void removeListener(Listener listener){
        listeners.remove(listener)
    }

    public void createEmails(){
        emailText = getNewEmailTextFromDataBase();
        emailSubject = getNewEmailTitleFromDataBase();

        notifyListeners();
    }

    public void notifyListeners(){
        for(Listener listener : listeners){
            listener.update(emailText, emailSubject);
        }
    }

    //Others methods
}
// All classes would have almost the same implementation with their own peculiarities.
public class GmailPattern implements Listener, EmailPattern {
    public void update(String emailText, String emailSubject){
        // apply class peculiarities
        sendEmail();
    }

    public void sendEmail(){
        // send the email
    }
}

Coloquei apenas o código do GmailPattern descrito acima para facilitar na leitura. Todas as outras classes como HotmailPattern, YahooPattern teriam os mesmos métodos variando apenas seu comportamento.

Veja como fica mais simples, coeso e funcional. Independente de qual o tipo de padrão do email a ser enviado (Gmail, Hotmail, Yahoo), nossa classe Notifier está notificando ao seu Listener que houve uma alteração de estado. Ela não precisa saber mais se agora estamos enviando para Gmail, Yahoo ou Hotmail. Viu como a classe EmailSender continuar realizando seu trabalho e de um modo mais “limpo”?

Repare que caso seja necessário atualizar o modo como a classe HotmailPattern envia o email, nossa classe EmailSender não sofrerá qualquer tipo de alteração. Isolando esse comportamento estamos tornando nosso programa mais flexível a mudanças.

Talvez você se pergunte: “Qual a vantagem do padrão Observer? Onde aplicar isso?” Vou colocar abaixo alguns exemplos:

  • Toda vez que um carro furar sinal vermelho, uma máquina irá tirar uma foto e enviar para o sistema. Teve que ter “alguém” que observou o fato e notificou ao sistema o que aconteceu.
  • Uma aplicação de mensagens online. Por exemplo: o antigo ICQ (mas vale para os atuais também), você tem a aplicação rodando em seu computador. Ao selecionar um contato de sua lista, você pode digitar uma mensagem e enviar. O ICQ em sua máquina notifica ao servidor essa mensagem. Ou o ICQ do contato busca no servidor se existem mensagens ou o servidor notifica o ICQ na máquina do usuário. Uma coisa é certa, não importa o que o seu contato esteja fazendo: “lendo fofoca de novela”, “vendo desenhos do Tom e Jerry no Youtube”, ele irá receber a mensagem.
  • Um mais simples ainda é: controle remoto. Existe uma aplicação que está aguardando o estímulo do controle. Assim que você pressiona o botão a aplicação entende a mensagem enviada e faz o que foi solicitado.

Mas ainda existe um acoplamento muito forte entre nossa classe EmailSender e suas listeners (GmailPattern,…). Olhe novamente o código mais acima e tente imaginar…

Para deixar esse acoplamento mais claro, vamos imaginar mais um requisito em nosso sistema:
Imagine que agora além da informação do “assunto” e do “conteúdo do texto” será necessário passar qual a conta do Yahoo usar. Com isso teremos um listener com um comportamento diferente dos demais.
Toda vez que precisarmos alterar a quantidade de parâmetros para enviar emails um Listener, YahooPattern por exemplo, terá que ser alterado e também um Notifier (EmailSender) sofrerá alterações.

Como melhorar isso? O ideal não é fazer com que alteração em uma classe não afete outra?

Veremos no próximo post sobre o assunto de Design Pattern outra solução para o tipo Observer.

Até a próxima pessoal. o_

Revisado por: Renan Alves dos Santos

Design Pattern – Strategy

Olá, tudo bem?

Vou aproveitar que estou estudando sobre design patterns e colocar aqui alguns padrões encontrados atualmente. O livro base é o Design Patterns – Head First. Vou descrever aqui os mesmos padrões encontrados no livro, mas estarei utilizando exemplos diferentes e acrescendo mais material na medida do possível.
Esse estudo para patterns serve para a certificação OCJD. Essa certificação exige a entrega de um sistema onde você deverá explicar o porquê de cada escolha sobre o seu design.

Vamos utilizar o seguinte caso de uso: “Um site de carros de brinquedos está recebendo bastantes visitas e se tornando famoso. Mas seus investidores querem mais, querem que agora seja possível fazer simulações das ações dos carros de brinquedo na web.”

Atualmente nosso modelo se divide da seguinte forma (existem várias outras subclasses de ToyCar que não foram descritas na imagem abaixo):

Design Pattern - Strategy

Agora os investidores desejam que todos os carros de brinquedo exerçam a função de ligar o motor, para que os clientes possam ouvir seu ronco. Para isso, a solução mais “simples, rápida e prática” teoricamente seria aplicar uma herança. Que mal há em utilizar esse ótimo artifício fornecido pelo Java? Basta criar o método ligar motor (“startEngine()”) em nosso modelo e tudo estará resolvido.

Design Pattern - Strategy

Após a entrega da nova funcionalidade, a equipe de desenvolvimento vibrava com o bom funcionamento ao ouvirem o ronco dos motores dos carros de brinquedo. MAS, uma ligação é recebida e um pequeno stress começa a aparecer. Um dos investidores ao navegar pela aplicação encontra algo que nunca deveria acontecer, um carro de madeira ligando motor. Por que isso aconteceu? Vejamos algo que passou despercebido:

Design Pattern - Strategy

Nosso carro de madeira, que também tem como superclasse a classe ToyCar, acabou por herdar essa função que não cabia a ele. O que fazer para resolver esse problema? Poderíamos sobrescrever o método herdado:

    @Override
    public boolean startEngine() {
        // Do nothing
    }

Seria essa a melhor saída? Supondo que temos uma classe para um carro de metal (“MetalCar”) também iríamos ter que sobrescrever esse método. Cada hora nosso projeto fica mais complexo, pois à medida que criarmos mais modelos de carros que não precisam ter o motor ligado, a sobrescrita do método de ligar o motor será obrigatória.

Qual outra solução seria possível aqui? Vamos aplicar um primeiro princípio a ser tratado nessa série de artigos:
Identifique tudo o que varia e separe do restante que não irá variar.”
O que isso significa? Veja que o comportamento de ligar motor pode ou não aparecer para determinadas classes, com isso poderíamos separar esse comportamento da nossa classe ToyCar. Ao invés de deixar nossa classe ToyCar controlando esse comportamento por que não delegamos essa função? Hora de ver, um segundo princípio de padrão de projeto:
Programe para interface.”
Esse conceito nada mais é do que: utilizar todo o benefício de herança. Imagine que se tivermos uma interface com um método conhecido, não iria fazer qualquer diferença para nossa classe qual a implementação de motor estiver sendo relacionada a ele. Vamos criar uma Interface para tratar o comportamento do motor?

Design Pattern - Strategy

E como ficariam nossas classes? Primeiramente a classe ToyCar, depois a classe Mercedes e por último, nossa classe carro de Madeira:

// Interface and Engine Behavior implementations

public interface EngineBehavior {
    public boolean startEngine();
}

public class NoEngineBehavior implements EngineBehavior {

    @Override
    public boolean startEngine() {
        System.out.println("No engine to turn on");
        return true;
    }
}

public class RegularEngineBehavior implements EngineBehavior {

    @Override
    public boolean startEngine() {
        System.out.println("Engine On");
        return true;
    }
}

public class PowerfulEngineBehavior implements EngineBehavior {
    
    @Override
    public boolean startEngine() {
        System.out.println("Powerfull Engine On");
        return true;
    }
}

// Our Classes Implmentatioins
public class ToyCar {
    private int doorTotal;
    private int maxSpeed;
    protected EngineBehavior engineBehavior;
    // Others attributes
    
    public ToyCar(){
        engineBehavior = new RegularEngine();
    }
    
    public void startEngine(){
        engineBehavior.startEngine();
    }
    
    // Others methods
}

public class Mercedes extends ToyCar {
    
    public Mercedes(){
        engineBehavior = new PowerfulEngineBehavior();
    }
    
    @Override
    public void startEngine(){
        engineBehavior.startEngine();
    }    
}

public class WoodCar extends ToyCar {
    
    public WoodCar(){
        engineBehavior = new NoEngineBehavior();
    }
    
    @Override
    public void startEngine(){
        engineBehavior.startEngine();
    }
}

Claro que poderíamos alterar o modo em que a Interface está recebendo sua classe implementada (“engineBehavior = new NoEngineBehavior();”) e outras questões relacionadas a patterns, mas isso será assunto para próximos artigos.

Talvez você possa pensar: “Espera aí, dá no mesmo se eu comentar o código “startEngine()” em todas as classes ou colocar o código engineBehavior = new NoEngineBehavior(); pois terei essa “repetição” em toda classe.” Pense comigo, e se ao chamar o método “startEngine()” para um tipo de ToyCar que não tenha motor, fosse necessário uma ação como registrar no log essa tentativa? Do modo como nossas classes estão implementadas, bastaria alterar o método da classe NoEngineBehavior e pronto. Todas nossas classes de carros que não tem motor já estarão com esse comportamento ativo.

Três considerações finais:

  • O nome de uma classe deve indicar um substantivo. Coisas em geral: “Carro”, “Casa”, “Computador”. Em nosso caso o comportamento (behavior) virou um substantivo. Ele é uma classe que determina um padrão de comportamento.
  • Programar para Interface: Não significa necessariamente criar sempre uma interface (“Interface”). Significa programar para um super tipo, ou seja, você poderia estar usando uma Classe normal, Classe Abstrata ou Interface. Você tem que adaptar sua necessidade à melhor opção possível. No livro do Use a Cabeça (Head First), os autores deixam isso claro. Não confundir esse conceito com sempre criar uma interface para qualquer situação, mas sim um super tipo que venha a solucionar o seu problema.
  • Acabamos por ver mais um princípio de projeto:
    "Dar prioridade à composição."

Como vimos no exemplo acima, colocamos nossa interface como composição, isso facilitou e muito nosso trabalho.
Padrão de Projeto: Strategy
Princípios vistos:

  • Identifique tudo o que varia e separe do restante que não irá variar.
  • Programe para interface.
  • Dar prioridade à composição.

Espero que esse post possa te ajudar. Qualquer dúvida ou colocação basta postar abaixo.

Até a próxima.
Revisores: João Luiz Silva Silvestre / Diego Prado