Ответ
Decorator и Adapter — оба структурные паттерны, работающие через композицию, но их цели противоположны.
Паттерн Decorator
Назначение: Динамически добавляет новую функциональность объекту, не изменяя его исходный класс. Следует принципу открытости/закрытости.
Как работает: Обёртывает исходный объект, делегируя ему вызовы и добавляя своё поведение до или после.
Пример на C#:
// Базовый компонент
public interface IDataSource
{
string ReadData();
void WriteData(string data);
}
// Конкретная реализация
public class FileDataSource : IDataSource { /* Чтение/запись в файл */ }
// Базовый декоратор
public abstract class DataSourceDecorator : IDataSource
{
protected IDataSource _wrappedDataSource;
protected DataSourceDecorator(IDataSource source) => _wrappedDataSource = source;
public virtual string ReadData() => _wrappedDataSource.ReadData();
public virtual void WriteData(string data) => _wrappedDataSource.WriteData(data);
}
// Конкретный декоратор — добавляет шифрование
public class EncryptionDecorator : DataSourceDecorator
{
public EncryptionDecorator(IDataSource source) : base(source) { }
public override string ReadData()
{
string encrypted = _wrappedDataSource.ReadData();
return Decrypt(encrypted); // Добавленное поведение
}
public override void WriteData(string data)
{
string encrypted = Encrypt(data); // Добавленное поведение
_wrappedDataSource.WriteData(encrypted);
}
private string Encrypt(string data) { /* ... */ }
private string Decrypt(string data) { /* ... */ }
}
// Использование: можно обернуть FileDataSource в EncryptionDecorator, а затем в CompressionDecorator.
Паттерн Adapter
Назначение: Преобразует интерфейс одного класса в интерфейс, ожидаемый клиентом. Используется для интеграции несовместимых компонентов.
Как работает: Обёртывает несовместимый объект (Adaptee) и реализует целевой интерфейс (Target), переводя вызовы.
Пример на C#:
// Целевой интерфейс, который ожидает клиент (новая система)
public interface IModernLogger
{
void Log(string message, LogLevel level);
}
// Устаревший класс, который нужно адаптировать (старая библиотека)
public class LegacyFileLogger
{
public void WriteToFile(string message) // Несовместимый метод
{
File.AppendAllText("log.txt", message + "n");
}
}
// Адаптер
public class LegacyLoggerAdapter : IModernLogger
{
private readonly LegacyFileLogger _legacyLogger;
public LegacyLoggerAdapter(LegacyFileLogger logger) => _legacyLogger = logger;
public void Log(string message, LogLevel level)
{
// Преобразуем вызов нового интерфейса в старый
string formattedMessage = $"[{DateTime.Now}] [{level}]: {message}";
_legacyLogger.WriteToFile(formattedMessage);
}
}
// Использование: клиент работает с IModernLogger, не зная о LegacyFileLogger.
Ключевые отличия
| Аспект | Decorator | Adapter |
|---|---|---|
| Цель | Расширить функциональность объекта. | Изменить интерфейс объекта для совместимости. |
| Взаимодействие | Декоратор и обёрнутый объект реализуют один и тот же интерфейс. | Адаптер реализует целевой интерфейс, но оборачивает объект с другим интерфейсом. |
| Время применения | Часто планируется изначально для гибкого добавления функций. | Применяется постфактум, когда нужно интегрировать чужой/устаревший код. |
| Изменение поведения | Добавляет новое поведение. | Преобразует (адаптирует) существующее поведение. |