Skip to content

Instantly share code, notes, and snippets.

@eltonvs
Last active May 8, 2017 12:57
Show Gist options
  • Save eltonvs/ac74a75f42ab1fb9c7df976646a6b3e1 to your computer and use it in GitHub Desktop.
Save eltonvs/ac74a75f42ab1fb9c7df976646a6b3e1 to your computer and use it in GitHub Desktop.

Design Patterns

Para estudar para a prova:

  • Singleton
  • Facade
  • Observer
  • Strategy
  • Template Method
  • Adapter
  • Composite
  • Abstract Factory

Singleton

Características:

  • Restringe a instanciação da classe
  • Haverão múltiplas referências para uma única instancia
  • Útil quando apenas apenas uma instancia de um objeto é necessária durante toda a execução do código

Exemplo de implementação em Java:

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }

        return instance;
    }
}

Facade

É um objeto que provê um interface simplificada para um código grande, como o de uma biblioteca.

Características:

  • Torna o código mais fácil de usar, testar e entender (pois possui métodos mais "convenientes" para tarefas "comuns")
  • Torna a biblioteca mais legível (pelo mesmo motivo acima)
  • Reduz as dependências externas sobre o funcionamento interno de uma biblioteca, uma vez que a maioria dos códigos utiliza facade, permitindo assim, mais flexibilidade no desenvolvimento do sistema
  • Agrupa uma coleção de APIs mal projetadas em uma única API bem projetada

É muito utilizada em sistemas muito complexos ou difíceis de entender por possuírem uma alta interdependência entre sistemas ou por não ter seu código disponível. Esse padrão oculta a complexidade em sistemas grandes e provê uma interface simples para o cliente. Geralmente envolve uma única classe "wrapper" que contém o conjunto de membros requeridos pelo cliente. Esses membros acessam o sistema com o comportamento do cliente Facade, ocultando os detalhes de implementação.

Um facade é utilizado quando é preciso uma interface mais fácil e simples para uma camada subjacente. Alternativamente pode ser utilizado um Adapter quando o "wrapper" precisa respeitar uma interface particular e suportar diferentes comportamentos.

Dessa forma, um facade é tipicamente utilizado quando:

  • Uma interface mais simples é necessária para acessar um sistema complexo
  • Um é muito complexo ou difícil de entender
  • Um ponto de entrada é necessário para cada nível do software em camadas
  • As abstrações e implementações de um subsistema são fortemente acopladas

Exemplo de implementação em Java

/* Partes complexas do sistema */
public class CPU {
    public void freeze() { ... }
    public void jump(long position) { ... }
    public void execute() { ... }
}

public class HardDrive {
    public byte[] read(long lba, int size) { ... }
}

public class Memory {
    public void load(long position, byte[] data) { ... }
}

/* Facade */
public class ComputerFacade {
    private CPU processor;
    private HardDrive hd;
    private Memory ram;

    public ComputerFacade() {
        this.processor = new CPU();
        this.hd = new HardDrive();
        this.ram = new Memory();
    }

    public void start() {
        processor.freeze();
        ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
        processor.jump(BOOT_ADDRESS);
        processor.execute();
    }
}

/* Client */
class Home {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start();
    }
}

Observer

Propósito:

  • Dependência de 1 para muitos entre objetos (quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente)

Assim, o "observador" se cadastra ao "sujeito" ("observável"), que notifica ao "observador" quaisquer mudanças que ocorram com ele. Com esse padrão, podemos ter múltiplos "observadores" cadastrados ao "sujeito", de forma que ele notifique a todos os "observadores" as mudanças em seu estado, sem nem mesmo presumir quem eles são.

Exemplo de implementação em Java

/* Observable */
public class Observable {
    private ArrayList<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notify() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
}

/* Observer */
public interface Observer {
    public void update(Observable observable);
}

/* Concrete Observer/Observable */
public class ObservableData extends Observable {
    private Object myData;

    public void setData(Object myData) {
        this.myData = mydata;
        notify();
    }

    public Object getData() {
        return myData;
    }
}

public class ConcreteObserver implements Observer {
    public void update(Observable observable) {
        ObservableData data = (ObservableData) observable;
        data.getData();
    }
}

Strategy

A.K.A. Policy Pattern

É um design pattern comportamental, que permite que o comportamento de um algoritmo seja selecionado em tempo de execução.

Ele consiste em:

  • Definir uma família de algoritmos
  • Encapsular cada algoritmo como uma classe
  • Tornar os algoritmos intercabiáveis nessa família

Assim, o algoritmo pode variar independentemente do cliente que irá utilizá-lo.

O maior incentivo para o uso desse padrão é a melhoria da manutenção do código. Para tanto, é necessário que seja definido um conjunto (família) de classes para que possam ser alteradas em tempo de execução. Dessa forma os objetos trabalham de forma independente para os clientes realizarem operações diferentes, sem depender da implementação do comportamento em outra classe.

Exemplo de implementação em Java

public interface CalculateStrategy {
    public double calculate(NeedToCalculate object);
}

/* Classes that implements CalculateStrategy */
public class CalculateStrategy1 implements CalculateStrategy {
    public double calculate(NeedToCalculate object) { ... }
}

public class CalculateStrategy2 implements CalculateStrategy {
    public double calculate(NeedToCalculate object) { ... }
}

public class CalculateStrategy3 implements CalculateStrategy {
    public double calculate(NeedToCalculate object) { ... }
}

/* Class that uses strategy */
public class NeedToCalculate {
    CalculateStrategy calculateStrategy;

    public NeedToCalculate(int type) {
        switch (type) {
            case 1:
                calculateStrategy = new CalculateStrategy1();
                break;
            case 2:
                calculateStrategy = new CalculateStrategy2();
                break;
            case 3:
                calculateStrategy = new CalculateStrategy3();
                break;
            default:
                throw new RuntimeException("Type not found.");
        }
    }

    public double calculate() {
        return calculateStrategy.calculate(this);
    }
}

Template Method

É a definição de um algoritmo com algumas partes do mesmo sendo definidos por métodos abstratos. Dessa forma, as subclasses devem se responsabilizar pela implementação dessas partes abstratas desse algoritmo, de forma que cada subclasse implemente de uma forma diferente, de acordo com a sua necessidade, oferecendo assim um comportamente concreto construindo todo o algoritmo.

Esse padrão oferece uma estrutura fixa de um algoritmo, de forma que a parte fixa (que chama os métodos abstratos) deve estar na superclasse, o uso de uma classe abstrata é obrigatório pois deve ser permitido a implementação de um método concreto (o que não ocorre em interfaces) e, nele, deve haver a chamada dos métodos abstratos, havendo assim a definição de como deve ser o fluxo de execução do algoritmo. Por ser uma classe abstrata, há também a vantagem da obrigatoriedade da implementação de seus métodos não concretos por parte da subclasse, com isso, certas partes do algoritmo podem ser preenchidas por implementações variáveis, postergando assim a definição de alguns passos do algoritmo para que outras classes possam redefini-los.

Exemplo da implementação em java

/* Abstract Class to define the algorithm structure */
public abstract class AbstractClass {
    public final void templateMethod() {
        ...
        primitiveOperation1();
        primitiveOperation2();
    }

    public abstract void primitiveOperation1();

    public abstract void primitiveOperation2();
}

/* Concrete Class 1 */
public class Concrete1 extends AbstractClass {
    public void primitiveOperation1() {
        ...
    }

    public void primitiveOperation2() {
        ...
    }
}

/* Concrete Class 2 */
public class Concrete2 extends AbstractClass {
    public void primitiveOperation1() {
        ...
    }

    public void primitiveOperation2() {
        ...
    }
}

Adapter

A.K.A. Wrapper

É um padrão que permite que duas interfaces "incompatíveis" possam funcionar em conjunto (funcionando assim como um adaptador entre as interfaces). Assim, as interfaces podem ser incompatíveis, mas funcionam em conjunto. Dessa forma, um adapter é basicamente um conversor de interfaces.

Exemplo de implementação em java

/* Original class */
public class PortugueseStream {
    public void setarDados(Object dado) { ... }

    public Object obeterDados() { ... }
}

/* Adapter class */
public class StreamAdapter {
    public PortugueseStream streamer;

    public StreamAdapter(PortugueseStream streamer) {
        this.streamer = streamer
    }

    public void setData(Object data) {
        streamer.setarDados(data);
    }

    public Object getData() {
        return streamer.obterDados();
    }
}

Composite

O composite é um padrão de projeto utilizado para representar um objeto formado pela composição de objetos similares. Esse conjunto de objetos pressupõe uma mesma hierarquia de classes a que ele pertence. Tal padrão é, normalmente utilizado para representar listas recursivas de elementos. Além disso, esse modo de representação hierárquica de classes permite que os elementos contidos em um objeto composto sejam tratados como se fossem um objeto único. Dessa forma, os métodos comuns às classes podem também ser aplicados ao conjunto agrupado no objeto composto.

A intenção desse padrão de projeto é compor objetos em estruturas de árvore para representar a hierarquia parte-todo.

Um exemplo prático é o da criação de interfaces gráficas, uma interface pode conter um ou mais ícones, caixas de texto, botões e vários outros elementos. Abstraindo um Elemento Gráfico, haveria então a superclasse comum a todos os elementos gráficos atômicos. Dessa forma, a interface pode ser ser representada como uma classe que contém zero ou mais elementos gráficos.

Exemplo de implementação em java

/* Component */
public interface Graphic {
    public void print();
}

/* "Leaf" */
public class Button implements Graphic {
    public void print() { ... }
}

/* Composite */
public class CompositeGraphic implements Graphic {
    private List<Graphic> childGraphics = new ArrayList<>();

    public void print() {
        for (Graphic graphic : childGraphics) {
            graphic.print();
        }
    }

    public void add(Graphic graphic) {
        childGraphics.add(graphic);
    }

    public void remove(Graphic graphic) {
        childGraphics.remove(graphic);
    }
}

/* Application */
public class Application {
    public static void main(String[] args) {
        // Create some buttons
        Button btn1 = new Button();
        Button btn2 = new Button();
        Button btn3 = new Button();
        Button btn4 = new Button();

        // Create Composite Graphics
        CompositeGraphic window = new CompositeGraphic();
        CompositeGraphic section1 = new CompositeGraphic();
        CompositeGraphic section2 = new CompositeGraphic();

        // Add buttons to window
        window.add(btn1);
        window.add(btn2);

        // Create sections
        section1.add(btn3);
        section2.add(btn4);

        // Add sections to window
        window.add(section1);
        window.add(section2);

        // Print window
        window.print();
    }
}

Abstract Factory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment