Bridge Pattern – Abstraksiya və İcranın Ayrılması
-
Bridge Pattern (Körpü nümunəsi) — proqramlaşdırmada abstraksiyanı (istifadəçi səviyyəsində interfeys) onun icrasından (daxili işləmə qaydasından) ayırmaq üçün istifadə olunan dizayn nümunəsidir. Bu, hər iki tərəfin müstəqil şəkildə inkişaf etdirilməsinə imkan yaradır.
Başqa sözlə, bu pattern “nə edirik” və “necə edirik” anlayışlarını bir-birindən ayırır.
🧪 JavaScript-də Bridge Pattern nümunəsi
// Abstraksiya class RemoteControl { constructor(device) { this.device = device; } togglePower() { if (this.device.isEnabled()) { this.device.disable(); } else { this.device.enable(); } } volumeUp() { this.device.setVolume(this.device.getVolume() + 10); } volumeDown() { this.device.setVolume(this.device.getVolume() - 10); } } // Genişləndirilmiş Abstraksiya class AdvancedRemoteControl extends RemoteControl { mute() { this.device.setVolume(0); } } // İcraçı interfeys class Device { constructor() { this.volume = 50; this.enabled = false; } isEnabled() { return this.enabled; } enable() { this.enabled = true; } disable() { this.enabled = false; } getVolume() { return this.volume; } setVolume(volume) { this.volume = volume; } } // Konkret İcraçılar class TV extends Device { constructor() { super(); } // TV-yə xas əlavə metodlar buraya əlavə oluna bilər } class Radio extends Device { constructor() { super(); } // Radioya xas əlavə metodlar buraya əlavə oluna bilər }
Koda təhlil ilə baxış
Komponent İzah RemoteControl
Abstraksiya rolunu oynayır. Obyekti idarə etmək üçün ümumi metodlar təklif edir, lakin icranı device
obyektinə ötürür.AdvancedRemoteControl
Abstraksiyanı genişləndirir və əlavə imkanlar təqdim edir (məsələn, mute
).Device
İcraçı (Implementor) interfeysidir. Konkret cihazların davranışlarını müəyyən edir. TV
vəRadio
Konkret icraçılar (Concrete Implementors). Device
sinfini miras alaraq öz spesifik funksiyalarını əlavə edə bilərlər.
️ İstifadə nümunəsi
const tv = new TV(); const remote = new RemoteControl(tv); remote.togglePower(); console.log(tv.isEnabled()); // true remote.volumeUp(); console.log(tv.getVolume()); // 60 const radio = new Radio(); const advancedRemote = new AdvancedRemoteControl(radio); advancedRemote.togglePower(); console.log(radio.isEnabled()); // true advancedRemote.mute(); console.log(radio.getVolume()); // 0
Burada
RemoteControl
istənilənDevice
tipli obyektlə işləyə bilir. Hər bir yeni cihaz üçünRemoteControl
-u dəyişməyə ehtiyac yoxdur.
🧠 Nəyə görə Bridge Pattern istifadə olunur?
- Kodun genişləndirilməsi asanlaşır.
- Yeni
Device
(məsələn, Projector) və ya yeniRemoteControl
variantları əlavə etmək mümkündür. - Abstraksiya və implementasiya bir-birindən asılı deyil, bu da çeviklik və test ediləbilərlik verir.
Ümumi Xülasə
Bridge Pattern, kodu aşağıdakı şəkildə strukturlaşdırmağa imkan verir:
RemoteControl
— abstraksiya təbəqəsi (istifadəçi interfeysi)AdvancedRemoteControl
— genişləndirilmiş abstraksiyaDevice
— implementasiya interfeysiTV
,Radio
— konkret implementasiyalar
Bu struktur yeni cihazlar və ya uzaqdan idarəetmə növləri əlavə etdikdə kodun dəyişməsini minimuma endirir.
Nəticə
Bridge Pattern dizayn şablonu kompleks sistemlərdə asan genişləndirmə və idarəetmə üçün idealdır. JavaScript kimi obyekt yönümlü yanaşmanı dəstəkləyən dinamik dillərdə bu şablon real dünyada geniş istifadə olunur.
Əgər siz komponentləriniz arasında zəif əlaqə (loose coupling) istəyirsinizsə, Bridge Pattern sizin üçün düzgün seçim ola bilər.
-
🧪 Tapşırıq
Aşağıdakı kod nümunəsində
Shape
adlı əsas class-danSquare
vəTriangle
class-ları yaradılır. Hər forma həmVector
(xəttlərlə) həm dəRaster
(piksel ilə) şəklində çəkilə bilər. Bu isə hər formanın iki fərqli versiyasının yaradılmasına səbəb olur:class Shape { constructor(name) { this.name = name; } } class Triangle extends Shape { constructor() { super('triangle'); } } class Square extends Shape { constructor() { super('square'); } } class VectorSquare extends Square { toString() { return 'Drawing square as lines'; } } class RasterSquare extends Square { toString() { return 'Drawing square as pixels'; } } // imagine VectorTriangle and RasterTriangle are here too
Bu yanaşma çox sayda class-ın yaradılmasına səbəb olur və sistemin genişləndirilməsini çətinləşdirir. Hər yeni forma və render üsulu üçün yeni bir class əlavə etmək lazım gəlir. Bu, məhz Dekart hasili problemini yaradır:
Formalar × Render üsulları = Çox sayda sinif
Yuxarıdakı kodu elə refactor edin ki, Bridge Pattern-dən istifadə edərək
VectorTriangle
vəRasterTriangle
yaradıb kodunew Triangle(new RasterRenderer());
formada çağırdıqda cavabındaDrawing Triangle as pixels
alınsın.
🧩 Həlli
Çalışın ilk öncə özünüz həll edin sonra həllinə baxın
Bu problemi Bridge Pattern ilə həll edə bilərik. Bu dizayn pattern-in məqsədi abstraksiyanı onun implementasiyasından ayırmaqdır. Bununla da hər iki tərəf müstəqil şəkildə inkişaf etdirilə bilər.// Renderer interfeysini təmsil edən baza class class Renderer { get whatToRenderAs() { throw new Error('whatToRenderAs not implemented'); } } // Konkret Renderer-lər class VectorRenderer extends Renderer { get whatToRenderAs() { return 'lines'; } } class RasterRenderer extends Renderer { get whatToRenderAs() { return 'pixels'; } } // Baz forma classı— artıq rendereri qəbul edir class Shape { constructor(name, renderer) { this.name = name; this.renderer = renderer; } toString() { return `Drawing ${this.name} as ${this.renderer.whatToRenderAs}`; } } // Konkret formalar — yalnız ad və renderer qəbul edir class Triangle extends Shape { constructor(renderer) { super('Triangle', renderer); } } class Square extends Shape { constructor(renderer) { super('Square', renderer); } }
️ İstifadə nümunəsi
const triangle = new Triangle(new RasterRenderer()); console.log(triangle.toString()); // Output: Drawing Triangle as pixels const square = new Square(new VectorRenderer()); console.log(square.toString()); // Output: Drawing Square as lines
Bilik paylaşdıqca artan bir sərvətdir