DEV Community

Higor Diego
Higor Diego

Posted on

6

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.

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay