Introdução aos padrões de design criacional

Introdução aos padrões de design criacional

1. Introdução

Na engenharia de software, um Design Pattern descreve uma solução estabelecida para os problemas mais comuns encontrados no design de software. Representa as melhores práticas desenvolvidas por um longo período por tentativa e erro por desenvolvedores de software experientes.

Design Patterns ganhou popularidade depois que o livroDesign Patterns: Elements of Reusable Object-Oriented Software foi publicado em 1994 por Erich Gamma, John Vlissides, Ralph Johnson e Richard Helm (também conhecido como Gang of Four ou GoF).

Neste artigo, vamos explorar os padrões de design de criação e seus tipos. Também veremos alguns exemplos de código e discutiremos as situações em que esses padrões se encaixam em nosso design.

2. Padrões de Design Criativo

Creational Design Patterns are concerned with the way in which objects are created. Eles reduzem as complexidades e a instabilidade criando objetos de maneira controlada.

O operadornew é frequentemente considerado prejudicial, pois espalha objetos por todo o aplicativo. Com o tempo, pode ser desafiador alterar uma implementação, porque as classes se tornam fortemente acopladas.

Os padrões de design criacional resolvem esse problema dissociando o cliente totalmente do processo de inicialização real.

Neste artigo, discutiremos quatro tipos de Padrão de Design Criativo:

  1. Singleton - Garante que, no máximo, apenas uma instância de um objeto exista em todo o aplicativo

  2. Método de fábrica - Cria objetos de várias classes relacionadas sem especificar o objeto exato a ser criado

  3. Abstract Factory - Cria famílias de objetos dependentes relacionados

  4. Builder Constrói objetos complexos usando uma abordagem passo a passo

Vamos agora discutir cada um desses padrões em detalhes.

3. Padrão de Design Singleton

O Singleton Design Pattern visa manter uma verificação na inicialização de objetos de uma classe particular porensuring that only one instance of the object exists throughout the Java Virtual Machine.

Uma classe Singleton também fornece um ponto de acesso global exclusivo ao objeto, para que cada chamada subseqüente ao ponto de acesso retorne apenas esse objeto específico.

3.1. Exemplo de padrão singleton

Embora o padrão Singleton tenha sido introduzido pelo GoF, a implementação original é conhecida por ser problemática em cenários multithread.

Então, aqui, vamos seguir uma abordagem mais ideal que faz uso de uma classe interna estática:

public class Singleton  {
    private Singleton() {}

    private static class SingletonHolder {
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

Aqui, criamos uma classe internastatic que contém a instância da classeSingleton. Ele cria a instância apenas quando alguém chama o métodogetInstance() e não quando a classe externa é carregada.

Essa é uma abordagem amplamente usada para uma classe Singleton, pois não requer sincronização, é segura para threads, aplica inicialização lenta e possui comparativamente menos clichê.

Além disso, observe que o construtor tem o modificador de acessoprivate. This is a requirement for creating a Singleton since a public constructor would mean anyone could access it and start creating new instances.

Lembre-se de que esta não é a implementação original do GoF. Para a versão original, visitethis linked example article em Singletons em Java.

3.2. Quando usar o Singleton Design Pattern

  • Para recursos caros de criar (como objetos de conexão com o banco de dados)

  • É uma boa prática manter todos os madeireiros como singletons, o que aumenta o desempenho

  • Classes que fornecem acesso às definições de configuração do aplicativo

  • Classes que contêm recursos que são acessados ​​no modo compartilhado

4. Padrão de Design de Método de Fábrica

O Factory Design Pattern ou o Factory Method Design Pattern é um dos padrões de design mais usados ​​em Java.

De acordo com GoF, este padrão“defines an interface for creating an object, but let subclasses decide which class to instantiate. O método Factory permite que uma classe adie a instanciação para subclasses ”.

Esse padrão delega a responsabilidade de inicializar uma classe do cliente para uma classe de fábrica específica, criando um tipo de construtor virtual.

Para isso, contamos com uma fábrica que nos fornece os objetos, ocultando os detalhes reais da implementação. Os objetos criados são acessados ​​usando uma interface comum.

4.1. Exemplo de padrão de projeto de método de fábrica

Neste exemplo, vamos criar uma interfacePolygon que será implementada por várias classes concretas. UmPolygonFactory será usado para buscar objetos desta família:

image

Vamos primeiro criar a interfacePolygon:

public interface Polygon {
    String getType();
}

A seguir, criaremos algumas implementações comoSquare,Triangle, etc. que implementam esta interface e retornam um objeto do tipoPolygon.

Agora podemos criar uma fábrica que tome o número de lados como argumento e retorne a implementação apropriada dessa interface:

public class PolygonFactory {
    public Polygon getPolygon(int numberOfSides) {
        if(numberOfSides == 3) {
            return new Triangle();
        }
        if(numberOfSides == 4) {
            return new Square();
        }
        if(numberOfSides == 5) {
            return new Pentagon();
        }
        if(numberOfSides == 7) {
            return new Heptagon();
        }
        else if(numberOfSides == 8) {
            return new Octagon();
        }
        return null;
    }
}

Observe como o cliente pode contar com essa fábrica para nos fornecer umPolygon apropriado, sem ter que inicializar o objeto diretamente.

4.2. Quando usar o padrão de design do método de fábrica

  • Quando é esperado que a implementação de uma interface ou classe abstrata mude frequentemente

  • Quando a implementação atual não pode acomodar confortavelmente novas mudanças

  • Quando o processo de inicialização é relativamente simples e o construtor requer apenas alguns parâmetros

5. Padrão de Design Abstrato de Fábrica

Na seção anterior, vimos como o padrão de design do Método de Fábrica poderia ser usado para criar objetos relacionados a uma única família.

Por outro lado, o Abstract Factory Design Pattern é usado para criar famílias de objetos relacionados ou dependentes. Às vezes também é chamada de fábrica de fábricas.

Para uma explicação detalhada, confira nosso tutorialAbstract Factory.

6. Padrão de Design do Construtor

O Builder Design Pattern é outro padrão criacional projetado para lidar com a construção de objetos comparativamente complexos.

Quando a complexidade da criação de objetos aumenta, o padrão Builder pode separar o processo de instanciação usando outro objeto (um construtor) para construir o objeto.

Esse construtor pode ser usado para criar muitas outras representações semelhantes usando uma abordagem passo a passo simples.

6.1. Exemplo de padrão de construtor

O Builder Design Pattern original introduzido pelo GoF se concentra na abstração e é muito bom ao lidar com objetos complexos; no entanto, o design é um pouco complicado.

Joshua Bloch, em seu livro Effective Java, introduziu uma versão melhorada do padrão do construtor que é limpo, altamente legível (porque faz uso defluent design) e fácil de usar da perspectiva do cliente. Neste exemplo, discutiremos essa versão.

Este exemplo tem apenas uma classe,BankAccount, que contém um construtor como uma classe internastatic:

public class BankAccount {

    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;

    // constructors/getters

    public static class BankAccountBuilder {
        // builder code
    }
}

Observe que todos os modificadores de acesso nos campos são declaradosprivate, uma vez que não queremos que objetos externos os acessem diretamente.

O construtor também éprivate para que apenas o Construtor atribuído a esta classe possa acessá-lo. Todas as propriedades definidas no construtor são extraídas do objeto construtor que fornecemos como argumento.

DefinimosBankAccountBuilder em uma classe internastatic:

public static class BankAccountBuilder {

    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;

    public BankAccountBuilder(String name, String accountNumber) {
        this.name = name;
        this.accountNumber = accountNumber;
    }

    public BankAccountBuilder withEmail(String email) {
        this.email = email;
        return this;
    }

    public BankAccountBuilder wantNewsletter(boolean newsletter) {
        this.newsletter = newsletter;
        return this;
    }

    public BankAccount build() {
        return new BankAccount(this);
    }
}

Observe que declaramos o mesmo conjunto de campos que a classe externa contém. Todos os campos obrigatórios são necessários como argumentos para o construtor da classe interna, enquanto os campos opcionais restantes podem ser especificados usando os métodos setter.

Essa implementação também suporta a abordagem de design fluente, fazendo com que os métodos setter retornem o objeto do construtor.

Finalmente, o método build chama o construtor privado da classe externa e passa a si próprio como argumento. OBankAccount retornado será instanciado com os parâmetros definidos porBankAccountBuilder.

Vejamos um exemplo rápido do padrão builder em ação:

BankAccount newAccount = new BankAccount
  .BankAccountBuilder("Jon", "22738022275")
  .withEmail("[email protected]")
  .wantNewsletter(true)
  .build();

6.2. Quando usar o padrão Builder

  1. Quando o processo envolvido na criação de um objeto é extremamente complexo, com muitos parâmetros obrigatórios e opcionais

  2. Quando um aumento no número de parâmetros do construtor leva a uma grande lista de construtores

  3. Quando o cliente espera representações diferentes para o objeto que é construído

7. Conclusão

Neste artigo, aprendemos sobre padrões de design criacionais em Java. Também discutimos seus quatro tipos diferentes, ou seja, Singleton, Factory Method, Abstract Factory e Builder Pattern, suas vantagens, exemplos e quando devemos usá-los.

Como sempre, os trechos de código completos sãoavailable over on GitHub.