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"
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));
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)