Padrão - Abstract Factory
Published: 2023-01-14 20:05:32


![Abstract Factory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xxyfhp18yq16xwob61e.jpg)


O padrão de projeto _Abstract Factory_ foi formalizado e descrito como um dos 23 padrões GoF (Gang of Four) no livro "Design Patterns: Elements of Reusable Object-Oriented Software" escrito por Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides em 1994.


O padrão _Abstract Factory_ é um padrão de projeto de software que permite criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas. Ele é semelhante ao padrão Factory Method, mas é um nível acima, ou seja, em vez de criar apenas um objeto, o padrão _Abstract Factory_ cria uma série de objetos relacionados.

O _Abstract Factory_ tem uma interface para criar cada tipo de objeto desejado, mas deixa as subclasses decidirem quais classes concretas serão usadas. Isso permite que você altere a família de objetos criados sem afetar as classes que usam esses objetos.

Um exemplo de uso de _Abstract Factory_ seria a criação de temas para um aplicativo, onde existem vários temas diferentes com botões, caixas de diálogo e outros elementos de interface do usuário. Cada tema é uma família de objetos relacionados, e o _Abstract Factory_ permitiria que você crie uma nova família de objetos para um novo tema sem afetar as classes que usam esses objetos.

O _Abstract Factory_ é um pouco mais complexo que o padrão de [Factory Method](https://dev.to/higordiego/padrao-factory-method-1k53), e é usado em situações em que existem várias famílias de objetos relacionados ou dependentes. Ele é útil para se trabalhar com sistemas complexos que possam ser divididos em sistemas menores e mais fáceis de entender.

Uma das principais vantagens do uso do padrão _Abstract Factory_ incluem:

- Abstração da criação de objetos: Como mencionado anteriormente, o _Abstract Factory_ permite que você crie objetos de diferentes tipos sem precisar saber a classe concreta que será usada para criá-los. Isso permite que você altere a família de objetos criados sem afetar as classes que usam esses objetos.
- Escalabilidade e flexibilidade: O padrão _Abstract Factory_ ajuda a manter o código escalável e flexível. Novas famílias de objetos podem ser adicionadas facilmente, sem afetar o código existente.
- Isolamento de lógica de criação: O _Abstract Factory_ permite que você isole a lógica de criação dos objetos da lógica do sistema, tornando-o mais fácil de modificar e manter.
- Reutilização de código: As classes concretas criadas pelo _Abstract Factory_ podem ser reutilizadas em outros lugares do sistema.


Segue o exemplo abaixo do padrão _Abstract Factory_ para criação de diferentes tipos de conexões com banco de dados em um software com nodejs.


```js

class Connection {
    constructor(type) {
        this.type = type
    }
    connect() {
        // lógica para conectar
    }
    query() {
        // lógica para fazer consultas
    }
    close() {
        // lógica para fechar a conexão
    }
}

class MySQLConnection extends Connection {
    constructor() {
        super('mysql')
    }
}

class MongoDBConnection extends Connection {
    constructor() {
        super('mongodb')
    }
}

class ConnectionFactory {
    createConnection(type) {
        switch (type) {
            case 'mysql':
                return new MySQLConnection()
            case 'mongodb':
                return new MongoDBConnection()
            default:
                throw new Error('Unknown connection type')
        }
    }
}

const factory = new ConnectionFactory()
const mysqlConnection = factory.createConnection('mysql')
mysqlConnection.connect()
mysqlConnection.query('SELECT * FROM users')
mysqlConnection.close()

const mongodbConnection = factory.createConnection('mongodb')
mongodbConnection.connect()
mongodbConnection.query({})
mongodbConnection.close()

```

A classe _"Connection"_ é uma classe abstrata que define a interface para as conexões com banco de dados. Ela possui métodos para conectar, fazer consultas e fechar a conexão.

As classes _"MySQLConnection"_ e _"MongoDBConnection"_ são as classes concretas que estendem a classe _"Connection"_ e implementam a lógica específica para cada tipo de conexão.
 
Elas também definem o tipo de conexão através da chamada a super no construtor.

A classe _"ConnectionFactory"_ é o _Abstract Factory_ propriamente dito, ela tem o método "createConnection" que é responsável por criar as instâncias de conexão de acordo com o tipo passado.

No final do código, é criada uma instância da classe _"ConnectionFactory"_ e é chamado o método createConnection, passando 'mysql' ou 'mongodb' e assim é criado uma instância específica de conexão para cada tipo. Depois disso, os métodos connect, query e close são chamados para as instâncias criadas.

_Simples, né ?_

Imagine outro cenário na qual a sua gestora de equipe informou que há uma necessidade de construção de uma dashboard cuja a finalidade é listar os usuários e os seus pedidos por meio de uma API interna.

Para resolver a seguinte problemática segue o exemplo abaixo:

```js
class API {
    constructor(baseURL) {
        this.baseURL = baseURL;
    }
    
    fetch(endpoint) {
        return fetch(`${this.baseURL}/${endpoint}`)
                    .then(response => response.json())
    }
}

class UsersAPI extends API {
    constructor() {
        super('https://my-app.com/api/users');
    }
    getUsers() {
        return this.fetch('users');
    }
}

class OrdersAPI extends API {
    constructor() {
        super('https://my-app.com/api/orders');
    }
    getOrders() {
        return this.fetch('orders');
    }
}

class APIFactory {
    createAPI(type) {
        switch (type) {
            case 'users':
                return new UsersAPI();
            case 'orders':
                return new OrdersAPI();
            default:
            throw new Error('Unknown API type');
        }
    }
}

const factory = new APIFactory();
const usersAPI = factory.createAPI('users');
usersAPI.getUsers().then(console.log);

const ordersAPI = factory.createAPI('orders');
ordersAPI.getOrders().then(console.log);

```

A classe _API_ é uma classe abstrata que define a interface para as APIs. Ela possui um construtor que recebe a URL base da API e um método fetch que faz uma requisição GET para a API, passando o endpoint específico.

As classes _UsersAPI_ e _OrdersAPI_ são classes concretas que estendem a classe API e implementam a lógica específica para cada tipo de API. Elas também definem a URL base da API através da chamada ao super() no construtor.

A classe _APIFactory_ é o _Abstract Factory_ propriamente dito, ela tem o método _createAPI_ que é responsável por criar as instâncias de API de acordo com o tipo passado.

No final do código, é criada uma instância da classe _APIFactory_ e é chamado o método _createAPI_, passando 'users' ou 'orders' e assim é criada uma instância específica de API para cada tipo. Depois disso, o método _getUsers()_ ou _getOrders()_ são chamados para as instâncias criadas, e esses métodos já retornam uma Promise que trata o retorno da API.

Você pode utilizar o padrão _Abstract Factory_ quando:

- Você tem várias famílias de objetos relacionadas ou dependentes, e precisa criar objetos dessas famílias sem especificar suas classes concretas;
- Você quer prover uma maneira simples de criar objetos, mas quer ocultar a lógica de criação da sua aplicação;
- Você quer prover uma maneira de alterar as famílias de objetos criados sem afetar as classes que usam esses objetos;
- Quando existe múltiplas combinações de tipos e estruturas de objetos, e essas combinações precisam ser criadas de forma organizada;
- Se você tem um sistema complexo que pode ser dividido em sistemas menores e mais fáceis de entender e quiser encapsular a lógica da criação desses sistemas menores;
- Se você está trabalhando com sistemas orientados a objetos.


**Conclusão**

Este padrão ajuda a manter o código escalável e flexível, pois novas famílias de objetos podem ser adicionadas facilmente, sem afetar o código existente. Ele também permite que a lógica de criação dos objetos seja isolada da lógica do sistema, tornando-o mais fácil de modificar e manter. 


Espero ter ajudado, até a próxima.