الگوی پل
الگوی پل، یک الگوی طراحی در مهندسی نرمافزار است که به معنای "جداسازی یک انتزاع از اجرای آن به طوری که این دو بتوانند به صورت مستقل تغییر پذیر باشند."میباشد. این الگو توسط باند چهار نفره (GoF) معرفی شده است.[1] الگوی پل از گروههای قالبدار و گروههای تجمعی و بعضاً ارثبری برای جداسازی مسئولیتها در طبقات مختلف استفاده میکند.
هنگامی که یک کلاس اغلب تغییر میکند، ویژگیهای برنامهنویسی شی گرا بسیار مفید خواهد بود، چرا که تغییرات در کد برنامه، میتواند با حداقل اطلاعات از برنامه صورت گیرد. زمانی که کلاسها و کاری که آنها انجام میدهند نسبت به یگدیگر تفاوتهای زیادی داشته باشد، از الگوی پل استفاده میشود. خود کلاس به عنوان انتزاع در نظر گرفته میشود و کاری که انجام میدهد در مرحله پیادهسازی آن است. همچنین الگوی پل میتواند به عنوان دو لایه از انتزاع در نظر گرفته شود.
زمانی که تنها یک پیادهسازی ثابت امکانپذیر است در زبان C++ این الگو در اصطلاح Pimpl شناخته میشود.
الگوی پل اغلب با الگوی آداپتور اشتباه گرفته میشود. در واقع، الگوی پل اغلب در زبان جاوا توسط کلاس الگوی آداپتور پیادهسازی میشود. مانند مثالی که در ادامه آمده است.
ساختار
- Abstraction (کلاس انتزاعی)
- رابط انتزاعی را تعریف میکند.
- مجری پیادهسازی را تأمین میکند.
- RefinedAbstraction (کلاس عادی)
- رابط تعریف شده در انتزاع را ارائه میدهد.
- Implementor (رابط کاربری)
- رابط کلاسهای پیادهسازی شده را تعریف میکند.
- ConcreteImplementor (کلاس عادی)
- رابط مجری پیادهسازی را پیادهسازی میکند.
مثال
C
الگوی پل در ساختار درخت شیءها را قرار میدهد. این الگو انتراع را از پیادهسازی جدا میکند. در این جا انتزاع نقش مشتری ای را اجرا میکند که اشیاء از آن فراخوانی میشوند. مثالی برای پیادهسازی در C# در ادامه آمده است:
// 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();
}
}
همان طوری که میبینید، کلاسهای پل، همگی از معماری رابط-محور متشابهی برای ساخت اشیاء استفاده میکنند. از طرف دیگر، انتزاع، یک شیء از مرحله پیادهسازی را گرفته و آن را اجرا میکند. در نتیجه، آنها را کاملاً از یکدیگر جدا میکند.
Crystal
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
خروجی
API1.circle at 1.0:2.0 - radius: 3.075
API2.circle at 5.0:7.0 - radius: 11.275
جاوا
این برنامه جاوا (SE 6) یک شکل (shape) را نشان میدهد.
/** "Implementor" */
interface DrawingAPI {
public void drawCircle(final double x, final double y, final double radius);
}
/** "ConcreteImplementor" 1/2 */
class DrawingAPI1 implements DrawingAPI {
public void drawCircle(final double x, final double y, final double radius) {
System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);
}
}
/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 implements DrawingAPI {
public void drawCircle(final double x, final double y, final double radius) {
System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);
}
}
/** "Abstraction" */
abstract class Shape {
protected DrawingAPI drawingAPI;
protected Shape(final DrawingAPI drawingAPI){
this.drawingAPI = drawingAPI;
}
public abstract void draw(); // low-level
public abstract void resizeByPercentage(final double pct); // high-level
}
/** "Refined Abstraction" */
class CircleShape extends Shape {
private double x, y, radius;
public CircleShape(final double x, final double y, final double radius, final DrawingAPI drawingAPI) {
super(drawingAPI);
this.x = x; this.y = y; this.radius = radius;
}
// low-level i.e. Implementation specific
public void draw() {
drawingAPI.drawCircle(x, y, radius);
}
// high-level i.e. Abstraction specific
public void resizeByPercentage(final double pct) {
radius *= (1.0 + pct/100.0);
}
}
/** "Client" */
class BridgePattern {
public static void main(final String[] args) {
Shape[] shapes = new Shape[] {
new CircleShape(1, 2, 3, new DrawingAPI1()),
new CircleShape(5, 7, 11, new DrawingAPI2())
};
for (Shape shape : shapes) {
shape.resizeByPercentage(2.5);
shape.draw();
}
}
}
خروجی آن:
API1.circle at 1.000000:2.000000 radius 3.075000
API2.circle at 5.000000:7.000000 radius 11.275000
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();
خروجی:
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()
}
}
}
جستارهای وابسته
- الگوهای طراحی
- آداپتور الگوی
- الگوی استراتژی
منابع
- Gamma, E, Helm, R, Johnson, R, Vlissides, J: Design Patterns, page 151.
پیوند به بیرون
- Bridge in UML and in LePUS3 (یک زبان مدل سازی فرمال)
- "C# Design Patterns: The Bridge Pattern". Sample Chapter. از: James W. Cooper. C# Design Patterns: A Tutorial. Addison-Wesley. ISBN 0-201-84453-2.