Wzorzec projektowy Dekorator, znany również jako nakładka lub wrapper, jest jednym z popularnych wzorców strukturalnych, który pozwala na dynamiczne dodawanie nowych obowiązków obiektom. Dzięki temu wzorcowi możliwe jest rozszerzanie funkcjonalności obiektów bez konieczności zmiany ich kodu wewnętrznego, co często wymagałoby rozbudowanej modyfikacji bazowych klas.
Cel Dekoratora
Głównym celem wzorca Dekorator jest umożliwienie dodawania nowych zachowań do istniejących obiektów w sposób dynamiczny. Działa on poprzez opakowywanie obiektów bazowych w dodatkowe obiekty zwane dekoratorami, które implementują ten sam interfejs co obiekty opakowywane. Dzięki tej specyfice każdy dekorator jest nieodróżnialny od obiektu bazowego z punktu widzenia klienta, co pozwala na łatwe komponowanie różnych zachowań.

Problem do Rozwiązania
Przykład, który chcielibyśmy rozważyć dotyczy logowania operacji w systemie. Załóżmy, że mamy aplikację, która wykonuje różne czynności i musimy rejestrować operacje w pliku logów. Bazowy logger oferuje podstawową funkcjonalność logowania do konsoli, jednak potrzeba więcej możliwości, takich jak logowanie do pliku lub dodawanie znaczników czasu, bez konieczności zmieniania samego loggera lub rozszerzania go za pomocą licznych podklas.
Rozwiązanie Dekoratora
Zamiast tworzyć wiele nowych podklas loggera, możemy skorzystać z wzorca Dekorator, aby stworzyć elastyczną i rozszerzalną strukturę logowania. Podstawowy logger pozostaje prosty, natomiast dodatkowe funkcje są implementowane jako dekoratory.
class Logger:
def log(self, message):
print(message)class FileLoggerDecorator(Logger):
def __init__(self, logger):
self._logger = loggerdef log(self, message):
self._logger.log(message)
with open('log.txt', 'a') as file:
file.write(message + 'n')class TimestampLoggerDecorator(Logger):
def __init__(self, logger):
self._logger = loggerdef log(self, message):
from datetime import datetime
timestamped_message = f"{datetime.now()}: {message}"
self._logger.log(timestamped_message)
Z tym systemem, klient może zdefiniować i stosować różne kombinacje logowania:
logger = Logger()
file_logger = FileLoggerDecorator(logger)
timestamp_logger = TimestampLoggerDecorator(file_logger)timestamp_logger.log("Wiadomość testowa")
Rezultat: wiadomość zostanie zarówno wypisana na konsoli z aktualnym znacznikiem czasu, jak i zapisana do pliku logu.

Korzyści Stosowania Dekoratora
- Dynamiczne dodawanie zachowań: Dekorator pozwala na zmianę lub rozszerzanie funkcjonalności obiektu w trakcie działania programu.
- Minimalna zmiana istniejącego kodu: Można wzbogacić program o nowe funkcjonalności bez modyfikacji istniejących klas.
Zgodność interfejsu: Wszystkie dekoratory są zgodne z interfejsem obiektu bazowego, co upraszcza ich stosowanie i zmniejsza ryzyko błędów.
Podsumowanie
Wzorzec Dekorator jest niezwykle przydatnym narzędziem dla programistów, którzy muszą dynamicznie rozszerzać możliwości obiektów. Dzięki wykorzystaniu opakowywania i zasadzie zgodności interfejsów, daje on dużą elastyczność bez wpływu na resztę kodu.