Паттерн Bridge (Мост) разделяет абстракцию и реализацию, позволяя им развиваться независимо друг от друга. Он особенно полезен, когда нужно избежать жёсткой зависимости между высокоуровневыми и низкоуровневыми компонентами.
Когда применять Bridge
- Когда нужно разделить большую систему на независимые уровни (например, UI и графический движок).
- Когда требуется менять реализацию объектов без изменения их интерфейсов.
- Когда классы растут в обе стороны — по типам и по вариантам реализации.
PlantUML-диаграмма
// Интерфейс реализации
public interface IDevice
{
bool IsEnabled { get; }
void Enable();
void Disable();
int GetVolume();
void SetVolume(int percent);
}
// Конкретная реализация — Радио
public class Radio : IDevice
{
private int _volume = 30;
public bool IsEnabled { get; private set; }
public void Enable() => IsEnabled = true;
public void Disable() => IsEnabled = false;
public int GetVolume() => _volume;
public void SetVolume(int percent) => _volume = percent;
}
// Конкретная реализация — Телевизор
public class Tv : IDevice
{
private int _volume = 50;
public bool IsEnabled { get; private set; }
public void Enable() => IsEnabled = true;
public void Disable() => IsEnabled = false;
public int GetVolume() => _volume;
public void SetVolume(int percent) => _volume = percent;
}
// Абстракция — пульт управления
public class Remote
{
protected IDevice _device;
public Remote(IDevice device)
{
_device = device;
}
public void TogglePower()
{
if (_device.IsEnabled)
_device.Disable();
else
_device.Enable();
}
public void VolumeDown()
{
_device.SetVolume(_device.GetVolume() - 10);
}
public void VolumeUp()
{
_device.SetVolume(_device.GetVolume() + 10);
}
}
// Расширенная абстракция — умный пульт
public class AdvancedRemote : Remote
{
public AdvancedRemote(IDevice device) : base(device) { }
public void Mute()
{
_device.SetVolume(0);
}
}
// Клиентский код
class Program
{
static void Main()
{
IDevice tv = new Tv();
Remote remote = new Remote(tv);
remote.TogglePower();
IDevice radio = new Radio();
AdvancedRemote advanced = new AdvancedRemote(radio);
advanced.TogglePower();
advanced.Mute();
Console.WriteLine($"TV включен: {tv.IsEnabled}, громкость: {tv.GetVolume()}");
Console.WriteLine($"Radio включено: {radio.IsEnabled}, громкость: {radio.GetVolume()}");
}
}
// Интерфейс реализации
type Device interface {
IsEnabled() bool
Enable()
Disable()
Volume() int
SetVolume(percent int)
}
type Radio struct {
enabled bool
volume int
}
func NewRadio() *Radio { return &Radio{volume: 30} }
func (r *Radio) IsEnabled() bool { return r.enabled }
func (r *Radio) Enable() { r.enabled = true }
func (r *Radio) Disable() { r.enabled = false }
func (r *Radio) Volume() int { return r.volume }
func (r *Radio) SetVolume(percent int) { r.volume = percent }
type Tv struct {
enabled bool
volume int
}
func NewTv() *Tv { return &Tv{volume: 50} }
func (t *Tv) IsEnabled() bool { return t.enabled }
func (t *Tv) Enable() { t.enabled = true }
func (t *Tv) Disable() { t.enabled = false }
func (t *Tv) Volume() int { return t.volume }
func (t *Tv) SetVolume(percent int) { t.volume = percent }
// Абстракция — пульт управления
type Remote struct {
device Device
}
func (r Remote) TogglePower() {
if r.device.IsEnabled() {
r.device.Disable()
} else {
r.device.Enable()
}
}
func (r Remote) VolumeDown() { r.device.SetVolume(r.device.Volume() - 10) }
func (r Remote) VolumeUp() { r.device.SetVolume(r.device.Volume() + 10) }
// Расширенная абстракция: встраивание вместо наследования
type AdvancedRemote struct {
Remote
}
func (r AdvancedRemote) Mute() { r.device.SetVolume(0) }
func main() {
tv := NewTv()
Remote{device: tv}.TogglePower()
radio := NewRadio()
advanced := AdvancedRemote{Remote{device: radio}}
advanced.TogglePower()
advanced.Mute()
fmt.Printf("TV включен: %t, громкость: %d\n", tv.IsEnabled(), tv.Volume())
fmt.Printf("Radio включено: %t, громкость: %d\n", radio.IsEnabled(), radio.Volume())
}
from typing import Protocol
class Device(Protocol):
def is_enabled(self) -> bool: ...
def enable(self) -> None: ...
def disable(self) -> None: ...
def get_volume(self) -> int: ...
def set_volume(self, percent: int) -> None: ...
class Radio:
def __init__(self) -> None:
self._enabled = False
self._volume = 30
def is_enabled(self) -> bool:
return self._enabled
def enable(self) -> None:
self._enabled = True
def disable(self) -> None:
self._enabled = False
def get_volume(self) -> int:
return self._volume
def set_volume(self, percent: int) -> None:
self._volume = percent
class Tv:
def __init__(self) -> None:
self._enabled = False
self._volume = 50
def is_enabled(self) -> bool:
return self._enabled
def enable(self) -> None:
self._enabled = True
def disable(self) -> None:
self._enabled = False
def get_volume(self) -> int:
return self._volume
def set_volume(self, percent: int) -> None:
self._volume = percent
class Remote:
def __init__(self, device: Device) -> None:
self._device = device
def toggle_power(self) -> None:
if self._device.is_enabled():
self._device.disable()
else:
self._device.enable()
def volume_down(self) -> None:
self._device.set_volume(self._device.get_volume() - 10)
def volume_up(self) -> None:
self._device.set_volume(self._device.get_volume() + 10)
class AdvancedRemote(Remote):
def mute(self) -> None:
self._device.set_volume(0)
tv = Tv()
Remote(tv).toggle_power()
radio = Radio()
advanced = AdvancedRemote(radio)
advanced.toggle_power()
advanced.mute()
print(f"TV включен: {tv.is_enabled()}, громкость: {tv.get_volume()}")
print(f"Radio включено: {radio.is_enabled()}, громкость: {radio.get_volume()}")
interface Device {
isEnabled(): boolean;
enable(): void;
disable(): void;
getVolume(): number;
setVolume(percent: number): void;
}
class Radio implements Device {
private enabled = false;
private volume = 30;
isEnabled(): boolean {
return this.enabled;
}
enable(): void {
this.enabled = true;
}
disable(): void {
this.enabled = false;
}
getVolume(): number {
return this.volume;
}
setVolume(percent: number): void {
this.volume = percent;
}
}
class Tv implements Device {
private enabled = false;
private volume = 50;
isEnabled(): boolean {
return this.enabled;
}
enable(): void {
this.enabled = true;
}
disable(): void {
this.enabled = false;
}
getVolume(): number {
return this.volume;
}
setVolume(percent: number): void {
this.volume = percent;
}
}
class Remote {
constructor(protected device: Device) {}
togglePower(): void {
if (this.device.isEnabled()) this.device.disable();
else this.device.enable();
}
volumeDown(): void {
this.device.setVolume(this.device.getVolume() - 10);
}
volumeUp(): void {
this.device.setVolume(this.device.getVolume() + 10);
}
}
class AdvancedRemote extends Remote {
mute(): void {
this.device.setVolume(0);
}
}
const tv = new Tv();
new Remote(tv).togglePower();
const radio = new Radio();
const advanced = new AdvancedRemote(radio);
advanced.togglePower();
advanced.mute();
console.log(`TV включен: ${tv.isEnabled()}, громкость: ${tv.getVolume()}`);
console.log(`Radio включено: ${radio.isEnabled()}, громкость: ${radio.getVolume()}`);
interface Device {
boolean isEnabled();
void enable();
void disable();
int getVolume();
void setVolume(int percent);
}
class Radio implements Device {
private boolean enabled;
private int volume = 30;
public boolean isEnabled() {
return enabled;
}
public void enable() {
enabled = true;
}
public void disable() {
enabled = false;
}
public int getVolume() {
return volume;
}
public void setVolume(int percent) {
volume = percent;
}
}
class Tv implements Device {
private boolean enabled;
private int volume = 50;
public boolean isEnabled() {
return enabled;
}
public void enable() {
enabled = true;
}
public void disable() {
enabled = false;
}
public int getVolume() {
return volume;
}
public void setVolume(int percent) {
volume = percent;
}
}
class Remote {
protected final Device device;
Remote(Device device) {
this.device = device;
}
void togglePower() {
if (device.isEnabled()) device.disable();
else device.enable();
}
void volumeDown() {
device.setVolume(device.getVolume() - 10);
}
void volumeUp() {
device.setVolume(device.getVolume() + 10);
}
}
class AdvancedRemote extends Remote {
AdvancedRemote(Device device) {
super(device);
}
void mute() {
device.setVolume(0);
}
}
public class Program {
public static void main(String[] args) {
Device tv = new Tv();
new Remote(tv).togglePower();
Device radio = new Radio();
AdvancedRemote advanced = new AdvancedRemote(radio);
advanced.togglePower();
advanced.mute();
System.out.printf("TV включен: %b, громкость: %d%n", tv.isEnabled(), tv.getVolume());
System.out.printf("Radio включено: %b, громкость: %d%n", radio.isEnabled(), radio.getVolume());
}
}
interface Device {
var isEnabled: Boolean
var volume: Int
}
class Radio : Device {
override var isEnabled = false
override var volume = 30
}
class Tv : Device {
override var isEnabled = false
override var volume = 50
}
open class Remote(protected val device: Device) {
fun togglePower() {
device.isEnabled = !device.isEnabled
}
fun volumeDown() {
device.volume -= 10
}
fun volumeUp() {
device.volume += 10
}
}
class AdvancedRemote(device: Device) : Remote(device) {
fun mute() {
device.volume = 0
}
}
fun main() {
val tv = Tv()
Remote(tv).togglePower()
val radio = Radio()
val advanced = AdvancedRemote(radio)
advanced.togglePower()
advanced.mute()
println("TV включен: ${tv.isEnabled}, громкость: ${tv.volume}")
println("Radio включено: ${radio.isEnabled}, громкость: ${radio.volume}")
}
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Позволяет изменять реализацию и абстракцию независимо | Усложняет структуру из-за необходимости введения дополнительных классов |
| Уменьшает количество подклассов при комбинации различных реализаций | Может быть избыточным при простых иерархиях |
| Повышает гибкость системы и следование принципам SOLID | Требует внимательного проектирования интерфейсов |
Заключение
Bridge помогает отделить логику управления (абстракцию) от конкретных реализаций, делая код масштабируемым и удобным для расширения. Это отличный выбор для систем, где требуется поддерживать множество вариантов поведения и платформ.