Padrão de ponte - Bridge pattern
O padrão de ponte é um padrão de design usado em engenharia de software que visa "separar uma abstração de sua implementação para que as duas possam variar independentemente" , introduzido pela Gangue dos Quatro . A ponte usa encapsulamento , agregação e pode usar herança para separar responsabilidades em classes diferentes .
Quando uma classe varia, muitas vezes, as características de programação orientada a objetos tornam-se muito útil porque as mudanças a um programa de código pode ser feito facilmente com um mínimo de conhecimentos sobre o programa. O padrão de ponte é útil quando a classe e o que ela faz variam com frequência. A própria classe pode ser considerada uma abstração e o que a classe pode fazer como implementação . O padrão de ponte também pode ser considerado como duas camadas de abstração.
Quando há apenas uma implementação fixa, esse padrão é conhecido como idioma Pimpl no mundo C ++ .
O padrão de ponte é frequentemente confundido com o padrão do adaptador e geralmente é implementado usando o padrão do adaptador de objeto ; por exemplo, no código Java abaixo.
Variante: a implementação pode ser desacoplada ainda mais, adiando a presença da implementação até o ponto em que a abstração é utilizada.
Visão geral
O padrão de design Bridge é um dos vinte e três padrões de design GoF bem conhecidos que descrevem como resolver problemas de design recorrentes para projetar software orientado a objeto flexível e reutilizável, ou seja, objetos que são mais fáceis de implementar, alterar, testar e reuso.
Que problemas o padrão de projeto do Bridge pode resolver?
- Uma abstração e sua implementação devem ser definidas e estendidas independentemente uma da outra.
- Uma vinculação de tempo de compilação entre uma abstração e sua implementação deve ser evitada para que uma implementação possa ser selecionada em tempo de execução.
Ao usar subclasses, diferentes subclasses implementam uma classe abstrata de maneiras diferentes. Mas uma implementação está ligada à abstração em tempo de compilação e não pode ser alterada em tempo de execução.
Que solução o padrão de design do Bridge descreve?
- Separe uma abstração (
Abstraction
) de sua implementação (Implementor
) colocando-as em hierarquias de classes separadas. - Implemente o
Abstraction
em termos de (delegando a) umImplementor
objeto.
Isso permite configurar um Abstraction
com um Implementor
objeto em tempo de execução.
Consulte também a classe Unified Modeling Language e o diagrama de sequência abaixo.
Estrutura
Classe UML e diagrama de sequência
No diagrama de classes da Unified Modeling Language acima , uma abstraction ( Abstraction
) não é implementada como de costume em uma única hierarquia de herança. Em vez disso, há uma hierarquia para uma abstração ( Abstraction
) e uma hierarquia separada para sua implementação ( Implementor
), o que torna as duas independentes uma da outra. A Abstraction
interface ( operation()
) é implementada em termos de (delegando a) a Implementor
interface ( imp.operationImp()
).
O diagrama de seqüência UML
mostra as interações em tempo de execução: O Abstraction1
objeto delega a implementação ao Implementor1
objeto (chamando operationImp()
em Implementor1
), que executa a operação e retorna para Abstraction1
.
Diagrama de classes
- Abstração (classe abstrata)
- define a interface abstrata
- mantém a referência do implementador.
- RefinedAbstraction (classe normal)
- estende a interface definida por Abstraction
- Implementador (interface)
- define a interface para classes de implementação
- ConcreteImplementor (classe normal)
- implementa a interface do Implementador
Exemplo
C #
O padrão de ponte compõe objetos na estrutura de árvore. Ele separa a abstração da implementação. Aqui, a abstração representa o cliente a partir do qual os objetos serão chamados. Um exemplo implementado em C # é fornecido abaixo
// Helps in providing truly decoupled architecture
public interface IBridge
{
void Function1();
void Function2();
}
public class Bridge1 : IBridge
{
public void Function1()
{
Console.WriteLine("Bridge1.Function1");
}
public void Function2()
{
Console.WriteLine("Bridge1.Function2");
}
}
public class Bridge2 : IBridge
{
public void Function1()
{
Console.WriteLine("Bridge2.Function1");
}
public void Function2()
{
Console.WriteLine("Bridge2.Function2");
}
}
public interface IAbstractBridge
{
void CallMethod1();
void CallMethod2();
}
public class AbstractBridge : IAbstractBridge
{
public IBridge bridge;
public AbstractBridge(IBridge bridge)
{
this.bridge = bridge;
}
public void CallMethod1()
{
this.bridge.Function1();
}
public void CallMethod2()
{
this.bridge.Function2();
}
}
As classes Bridge são a implementação que usa a mesma arquitetura orientada a interface para criar objetos. Por outro lado, a abstração pega uma instância da classe de implementação e executa seu método. Assim, eles estão completamente separados um do outro.
Cristal
abstract class DrawingAPI
abstract def draw_circle(x : Float64, y : Float64, radius : Float64)
end
class DrawingAPI1 < DrawingAPI
def draw_circle(x : Float, y : Float, radius : Float)
"API1.circle at #{x}:#{y} - radius: #{radius}"
end
end
class DrawingAPI2 < DrawingAPI
def draw_circle(x : Float64, y : Float64, radius : Float64)
"API2.circle at #{x}:#{y} - radius: #{radius}"
end
end
abstract class Shape
protected getter drawing_api : DrawingAPI
def initialize(@drawing_api)
end
abstract def draw
abstract def resize_by_percentage(percent : Float64)
end
class CircleShape < Shape
getter x : Float64
getter y : Float64
getter radius : Float64
def initialize(@x, @y, @radius, drawing_api : DrawingAPI)
super(drawing_api)
end
def draw
@drawing_api.draw_circle(@x, @y, @radius)
end
def resize_by_percentage(percent : Float64)
@radius *= (1 + percent/100)
end
end
class BridgePattern
def self.test
shapes = [] of Shape
shapes << CircleShape.new(1.0, 2.0, 3.0, DrawingAPI1.new)
shapes << CircleShape.new(5.0, 7.0, 11.0, DrawingAPI2.new)
shapes.each do |shape|
shape.resize_by_percentage(2.5)
puts shape.draw
end
end
end
BridgePattern.test
Resultado
API1.circle at 1.0:2.0 - radius: 3.075 API2.circle at 5.0:7.0 - radius: 11.275
C ++
#include <iostream>
#include <string>
#include <vector>
class DrawingAPI {
public:
virtual ~DrawingAPI() = default;
virtual std::string DrawCircle(float x, float y, float radius) const = 0;
};
class DrawingAPI01 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API01.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
};
class DrawingAPI02 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API02.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
};
class Shape {
public:
Shape(const DrawingAPI& drawing_api) : drawing_api_(drawing_api) {}
virtual ~Shape() = default;
virtual std::string Draw() const = 0;
virtual float ResizeByPercentage(const float percent) = 0;
protected:
const DrawingAPI& drawing_api_;
};
class CircleShape: public Shape {
public:
CircleShape(float x, float y, float radius, const DrawingAPI& drawing_api)
: Shape(drawing_api), x_(x), y_(y), radius_(radius) {}
std::string Draw() const override {
return drawing_api_.DrawCircle(x_, y_, radius_);
}
float ResizeByPercentage(const float percent) override {
return radius_ *= (1.0f + percent/100.0f);
}
private:
float x_, y_, radius_;
};
int main(int argc, char** argv) {
std::vector<CircleShape> shapes {
CircleShape{1.0f, 2.0f, 3.0f, DrawingAPI01{}},
CircleShape{5.0f, 7.0f, 11.0f, DrawingAPI02{}}
};
for (auto& shape: shapes) {
shape.ResizeByPercentage(2.5);
std::cout << shape.Draw() << std::endl;
}
return 0;
}
Resultado:
API01.circle at 1.000000:2.000000 - radius: 3.075000 API02.circle at 5.000000:7.000000 - radius: 11.275000
Java
O programa Java a seguir define uma conta bancária que separa as operações da conta do registro dessas operações.
// Logger has two implementations: info and warning
@FunctionalInterface
interface Logger {
void log(String message);
static Logger info() {
return message -> System.out.println("info: " + message);
}
static Logger warning() {
return message -> System.out.println("warning: " + message);
}
}
abstract class AbstractAccount {
private Logger logger = Logger.info();
public void setLogger(Logger logger) {
this.logger = logger;
}
// the logging part is delegated to the Logger implementation
protected void operate(String message, boolean result) {
logger.log(message + " result " + result);
}
}
class SimpleAccount extends AbstractAccount {
private int balance;
public SimpleAccount(int balance) {
this.balance = balance;
}
public boolean isBalanceLow() {
return balance < 50;
}
public void withdraw(int amount) {
boolean shouldPerform = balance >= amount;
if (shouldPerform) {
balance -= amount;
}
operate("withdraw " + amount, shouldPerform);
}
}
public class BridgeDemo {
public static void main(String[] args) {
SimpleAccount account = new SimpleAccount(100);
account.withdraw(75);
if (account.isBalanceLow()) {
// you can also change the Logger implementation at runtime
account.setLogger(Logger.warning());
}
account.withdraw(10);
account.withdraw(100);
}
}
Irá produzir:
info: withdraw 75 result true warning: withdraw 10 result true warning: withdraw 100 result false
PHP
interface DrawingAPI
{
function drawCircle($x, $y, $radius);
}
class DrawingAPI1 implements DrawingAPI
{
public function drawCircle($x, $y, $radius)
{
echo "API1.circle at $x:$y radius $radius.\n";
}
}
class DrawingAPI2 implements DrawingAPI
{
public function drawCircle($x, $y, $radius)
{
echo "API2.circle at $x:$y radius $radius.\n";
}
}
abstract class Shape
{
protected $drawingAPI;
public abstract function draw();
public abstract function resizeByPercentage($pct);
protected function __construct(DrawingAPI $drawingAPI)
{
$this->drawingAPI = $drawingAPI;
}
}
class CircleShape extends Shape
{
private $x;
private $y;
private $radius;
public function __construct($x, $y, $radius, DrawingAPI $drawingAPI)
{
parent::__construct($drawingAPI);
$this->x = $x;
$this->y = $y;
$this->radius = $radius;
}
public function draw()
{
$this->drawingAPI->drawCircle($this->x, $this->y, $this->radius);
}
public function resizeByPercentage($pct)
{
$this->radius *= $pct;
}
}
class Tester
{
public static function main()
{
$shapes = array(
new CircleShape(1, 3, 7, new DrawingAPI1()),
new CircleShape(5, 7, 11, new DrawingAPI2()),
);
foreach ($shapes as $shape) {
$shape->resizeByPercentage(2.5);
$shape->draw();
}
}
}
Tester::main();
Resultado:
API1.circle at 1:3 radius 17.5 API2.circle at 5:7 radius 27.5
Scala
trait DrawingAPI {
def drawCircle(x: Double, y: Double, radius: Double)
}
class DrawingAPI1 extends DrawingAPI {
def drawCircle(x: Double, y: Double, radius: Double) = println(s"API #1 $x $y $radius")
}
class DrawingAPI2 extends DrawingAPI {
def drawCircle(x: Double, y: Double, radius: Double) = println(s"API #2 $x $y $radius")
}
abstract class Shape(drawingAPI: DrawingAPI) {
def draw()
def resizePercentage(pct: Double)
}
class CircleShape(x: Double, y: Double, var radius: Double, drawingAPI: DrawingAPI)
extends Shape(drawingAPI: DrawingAPI) {
def draw() = drawingAPI.drawCircle(x, y, radius)
def resizePercentage(pct: Double) { radius *= pct }
}
object BridgePattern {
def main(args: Array[String]) {
Seq (
new CircleShape(1, 3, 5, new DrawingAPI1),
new CircleShape(4, 5, 6, new DrawingAPI2)
) foreach { x =>
x.resizePercentage(3)
x.draw()
}
}
}
Pitão
"""
Bridge pattern example.
"""
from abc import ABCMeta, abstractmethod
NOT_IMPLEMENTED = "You should implement this."
class DrawingAPI:
__metaclass__ = ABCMeta
@abstractmethod
def draw_circle(self, x, y, radius):
raise NotImplementedError(NOT_IMPLEMENTED)
class DrawingAPI1(DrawingAPI):
def draw_circle(self, x, y, radius):
return f"API1.circle at {x}:{y} - radius: {radius}"
class DrawingAPI2(DrawingAPI):
def draw_circle(self, x, y, radius):
return f"API2.circle at {x}:{y} - radius: {radius}"
class DrawingAPI3(DrawingAPI):
def draw_circle(self, x, y, radius):
return f"API3.circle at {x}:{y} - radius: {radius}"
class Shape:
__metaclass__ = ABCMeta
drawing_api = None
def __init__(self, drawing_api):
self.drawing_api = drawing_api
@abstractmethod
def draw(self):
raise NotImplementedError(NOT_IMPLEMENTED)
@abstractmethod
def resize_by_percentage(self, percent):
raise NotImplementedError(NOT_IMPLEMENTED)
class CircleShape(Shape):
def __init__(self, x, y, radius, drawing_api):
self.x = x
self.y = y
self.radius = radius
super(CircleShape, self).__init__(drawing_api)
def draw(self):
return self.drawing_api.draw_circle(self.x, self.y, self.radius)
def resize_by_percentage(self, percent):
self.radius *= 1 + percent / 100
class BridgePattern:
@staticmethod
def test():
shapes = [
CircleShape(1.0, 2.0, 3.0, DrawingAPI1()),
CircleShape(5.0, 7.0, 11.0, DrawingAPI2()),
CircleShape(5.0, 4.0, 12.0, DrawingAPI3()),
]
for shape in shapes:
shape.resize_by_percentage(2.5)
print(shape.draw())
BridgePattern.test()
Veja também
Referências
links externos
- Bridge em UML e em LePUS3 (uma linguagem de modelagem formal)
- Padrões de projeto C #: o padrão de ponte . Capítulo de amostra . 20/12/2002. De: James W. Cooper (2003). Padrões de projeto C #: um tutorial . Addison-Wesley . ISBN 0-201-84453-2 .