DEV Community

Higor Diego
Higor Diego

Posted on

Padrão - Bridge

Pattern Bridge

O padrão Bridge é um dos padrões de projeto estruturais que permite que a abstração e a implementação sejam variadas independentemente. Ele foi introduzido no livro "Design Patterns: Elements of Reusable Object-Oriented Software" de Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, conhecido como "Gang of Four" (GoF) em 1994.

O padrão Bridge é um dos padrões de projeto estruturais que permite separar a abstração de sua implementação, de forma que ambas possam ser variadas independentemente. Ele permite que você altere a implementação de uma classe sem afetar as classes que a usam.

A ideia principal do Bridge é criar uma interface abstrata que defina a funcionalidade da classe, e uma implementação concreta que forneça a lógica para essa funcionalidade. A classe abstrata contém uma referência à classe de implementação, e as classes concretas implementam essa interface.

Dessa forma, ao alterar a implementação de uma classe, somente a classe de implementação precisa ser modificada, as classes que usam a interface abstrata não precisam ser afetadas.

O padrão Bridge pode ser utilizado em várias situações, algumas das quais incluem:

  • Quando você tem uma classe abstrata que precisa ser reutilizada, mas sua implementação precisa ser modificada com frequência. O Bridge permite que você altere a implementação sem afetar a classe abstrata.
  • Quando você precisa trabalhar com classes que possuem diferentes implementações de uma mesma funcionalidade. O Bridge permite que você altere a implementação sem afetar as classes que usam a funcionalidade.
  • Quando você deseja criar uma classe abstrata para uma funcionalidade, mas deseja manter a flexibilidade de mudar a implementação sem afetar as classes que a usam.
  • Quando você deseja aumentar a escalabilidade do seu código, permitindo que você adicione novas implementações sem afetar as classes que já usam a funcionalidade.
  • Quando você precisa trabalhar com diferentes plataformas ou sistemas operacionais e precisa manter a flexibilidade de mudar a implementação sem afetar as classes que a usam.

Segue abaixo um simples exemplo de código usando o padrão Bridge.

class Abstract {
  constructor(implementation) {
    this.implementation = implementation;
  }

  execute() {
    return `${this.implementation.executeImplementation()}`;
  }
}

class Implementation {
  executeImplementation() {
    return 'execute Implementation';
  }
}

class ExtendedAbstract extends Abstract {
  execute() {
    return `ExtendedAbstract: ${this.implementation.executeImplementation()}`;
  }
}

class ConcreteImplementationA extends Implementation {
  executeImplementation() {
    return 'ConcreteImplementationA';
  }
}

class ConcreteImplementationB extends Implementation {
  executeImplementation() {
    return 'ConcreteImplementationB';
  }
}

const implementationA = new ConcreteImplementationA();
const AbstractA = new Abstract(implementationA);
console.log(AbstractA.execute()); // "ConcreteImplementationA"


const implementationB = new ConcreteImplementationB();
const AbstractB = new Abstract(implementationB);
console.log(AbstractB.execute()); // "ConcreteImplementationB"


const AbstractC = new ExtendedAbstract(implementationB);
console.log(AbstractC.execute()); // "ExtendedAbstract: ConcreteImplementationB"

Enter fullscreen mode Exit fullscreen mode

A classe Abstract é uma classe abstrata que define a interface para a operação. Ela contém um construtor que recebe uma instância de uma classe de implementação e armazena essa referência em uma propriedade de instância. Ela também tem um método execute que usa a referência de implementação para chamar o método de implementação executeImplementation.

A classe Implementation é uma classe de implementação que define o método executeImplementation, que retorna uma string "execute Implementation".

A classe ExtendedAbstract é uma classe que estende a classe Abstract e sobrescreve o método execute. Ele retorna uma string diferente, mas ainda usa a referência de implementação para chamar o método de implementação executeImplementation.

As classes ConcreteImplementationA e ConcreteImplementationB são classes concretas que estendem a classe Implementation e sobrescrevem o método executeImplementation para retornar strings diferentes.

O código cria duas instâncias de ConcreteImplementationA e ConcreteImplementationB e as passa para as instâncias de Abstract e ExtendedAbstract respectivamente. Em seguida, ele chama o método execute dessas instâncias e imprime os resultados.

Este exemplo mostra como o padrão Bridge permite separar a abstração da implementação, de forma que ambas possam ser variadas independentemente. Isso permite que você altere a implementação sem afetar a classe abstrata, aumentando a flexibilidade e escalabilidade do código.

Simples, né ?

Imagine outro cenário no qual precisa realizar uma busca de um post de um usuário em formato REST e outra busca em formato de Graphql por meio de API's diferentes na mesma implementação.

Segue a solução abaixo:


const axios = require('axios')

class AbstractAPI {
  constructor(implementation) {
    this.implementation = implementation;
  }

  getData(options) {
    return this.implementation.getDataImplementation(options);
  }
}

class APIImplementation {
  getDataImplementation() {
    throw new Error('getDataImplementation method must be implemented');
  }
}

class RESTAPI extends APIImplementation {
  constructor(url) {
    super();
    this.url = url;
  }

  getDataImplementation(options) {
    return axios.get(this.url, { params: options });
  }
}

class GraphQLAPI extends APIImplementation {
  constructor(url) {
    super();
    this.url = url;
  }

  getDataImplementation(options) {
    return axios.post(this.url, {query: options.query, variables: options.variables});
  }
}

const restAPI = new RESTAPI('https://jsonplaceholder.typicode.com/posts');
const restAPIBridge = new AbstractAPI(restAPI);
restAPIBridge.getData({ id: 1 }).then(response => console.log(response.data));

const graphQLAPI = new GraphQLAPI('https://countries.trevorblades.com/');
const graphQLBridge = new AbstractAPI(graphQLAPI);
graphQLBridge.getData({query: '{"query":"{\n  country(code: \"BR\") {\n    name\n  }\n}"}'}).then(response => console.log(response.data));

Enter fullscreen mode Exit fullscreen mode

Neste exemplo, a classe AbstractAPI é a classe abstrata que define a interface para o método de obtenção de dados e contém uma referência a uma implementação. As classes RESTAPI e GraphQLAPI são as classes concretas que estendem a classe APIImplementation e implementam o método getDataImplementation para fazer solicitações diferentes às suas respectivas APIs.

As instâncias de RESTAPI e GraphQLAPI são passadas para instâncias de AbstractAPI, que então são usadas para fazer solicitações às APIs, permitindo que você troque facilmente entre diferentes implementações sem alterar o código que as usa.

O padrão Bridge é útil em situações onde você precisa desacoplar uma abstração de sua implementação. Isso permite que você altere a implementação sem afetar a abstração e permite que você use várias implementações diferentes de uma abstração. Alguns exemplos de onde o padrão Bridge pode ser útil incluem:

  • Quando você deseja criar uma biblioteca ou componente que possa ser usado em diferentes sistemas operacionais ou plataformas sem mudar o código da biblioteca.
  • Quando você deseja usar diferentes tipos de banco de dados, como MySQL, PostgreSQL ou SQLite, mas deseja manter o código de acesso ao banco de dados desacoplado do seu código de negócios.
  • Quando você deseja usar diferentes tipos de renderer para desenhar gráficos em diferentes plataformas, como DirectX ou OpenGL.
  • Quando você deseja usar diferentes tipos de APIs, como REST ou GraphQL, mas deseja manter o código de consumo de API desacoplado do seu código de negócios.
  • Quando você deseja usar diferentes tipos de comunicação, como serial ou USB, mas deseja manter o código de comunicação desacoplado do seu código de negócios.

Conclusão

O padrão Bridge é um padrão de projeto de software que visa desacoplar uma abstração de sua implementação. Ele faz isso criando uma classe abstrata que define a interface para a abstração e uma classe de implementação que contém a lógica de implementação.

Espero ter ajudado, até a próxima.

Top comments (0)