Ответ
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 |
|---|---|---|
| Цель | Расширить функциональность объекта. | Изменить интерфейс объекта для совместимости. |
| Взаимодействие | Декоратор и обёрнутый объект реализуют один и тот же интерфейс. | Адаптер реализует целевой интерфейс, но оборачивает объект с другим интерфейсом. |
| Время применения | Часто планируется изначально для гибкого добавления функций. | Применяется постфактум, когда нужно интегрировать чужой/устаревший код. |
| Изменение поведения | Добавляет новое поведение. | Преобразует (адаптирует) существующее поведение. |
Ответ 18+ 🔞
А, слушай, вот тебе наглядная разница между Декоратором и Адаптером, чтобы в голове не каша была. Это как сравнивать тюнингованную тачку и переходник для розетки в чужой стране — оба что-то оборачивают, но цели у них, блядь, с разных планет.
Декоратор (Decorator) — это когда ты хочешь НАВЕСИФИГАРИТЬ
Суть: У тебя есть базовый объект, и ты хочешь его обогатить новыми фичами, но так, чтобы его родной код остался святым и неприкосновенным. Как будто ты кастомизируешь свой комп: поставил подсветку, потом водяное охлаждение, потом ещё вентиляторов нахуячил. Каждый новый слой добавляет функционал, но комп-то остаётся тем же самым — ты в него игры запускаешь.
Как работает на пальцах: Берёшь объект, заворачиваешь его в декоратор, и этот декоратор делает всё то же самое, что и оригинал, плюс что-то своё сверху. И так можно оборачивать в декораторы бесконечно, как матрёшку.
Вот смотри, пример на C#:
// Это наш базовый интерфейс. Допустим, это просто "Источник данных".
public interface IDataSource
{
string ReadData();
void WriteData(string data);
}
// Конкретная реализация — работает с файлом. Всё просто.
public class FileDataSource : IDataSource
{
// ... тут чтение/запись в файл, обычный такой функционал
}
// А вот базовый декоратор. Он тоже IDataSource, но внутри у него спрятан другой 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()
{
// 1. Сначала читаем данные у обёрнутого объекта (например, FileDataSource)
string encrypted = _wrappedDataSource.ReadData();
// 2. А потом ДОБАВЛЯЕМ своё — расшифровываем.
return Decrypt(encrypted);
}
public override void WriteData(string data)
{
// 1. Сначала ДОБАВЛЯЕМ своё — шифруем данные.
string encrypted = Encrypt(data);
// 2. Потом передаём зашифрованное обёрнутому объекту.
_wrappedDataSource.WriteData(encrypted);
}
private string Encrypt(string data) { /* ... */ }
private string Decrypt(string data) { /* ... */ }
}
// Использовать — одно удовольствие:
IDataSource source = new FileDataSource();
source = new EncryptionDecorator(source); // Обернули в шифрование
// А можно и ещё! source = new CompressionDecorator(source); // Поверх шифрования навесили сжатие
// И всё работает, как будто так и было задумано.
Итог по Декоратору: Ты не меняешь старую функциональность, ты её наращиваешь, как слоёный пирог. Все участники говорят на одном языке (один интерфейс).
Адаптер (Adapter) — это когда надо СВЯЗАТЬ ХУЙ С ПАЛЬЦЕМ
Суть: У тебя есть какой-то старый, кривой, легаси-класс с ебанутым интерфейсом, а твоя новая система требует от него совсем другого. Задача адаптера — сделать вид, что этот старый класс и есть тот самый нужный интерфейс. Это как взять вилку от советского утюга и через переходник воткнуть её в евро-розетку. Ты не меняешь утюг, ты меняешь вид его подключения.
Как работает на пальцах: Адаптер оборачивает объект с НЕПОДХОДЯЩИМ интерфейсом и представляет его миру как объект с ПРАВИЛЬНЫМ интерфейсом. Внутри адаптера происходит всякая грязная работа по переводу вызовов.
Держи пример:
// Это новый, современный интерфейс, который ждёт твоя система.
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;
// И вот здесь происходит подмена. Клиент думает, что вызывает Log(...),
// а адаптер это преобразует в вызов старого метода WriteToFile.
public void Log(string message, LogLevel level)
{
// Преобразуем вызов нового интерфейса в формат, понятный старой библиотеке.
string formattedMessage = $"[{DateTime.Now}] [{level}]: {message}";
_legacyLogger.WriteToFile(formattedMessage); // Вот и всё, магия адаптации.
}
}
// Использование:
LegacyFileLogger oldShittyLogger = new LegacyFileLogger();
IModernLogger logger = new LegacyLoggerAdapter(oldShittyLogger); // Обернули в адаптер
// Теперь вся система работает с logger через IModernLogger, даже не подозревая,
// что внутри булькает древний код.
Итог по Адаптеру: Ты не добавляешь новую функциональность, ты притворяешься. Ты берёшь один интерфейс и подсовываешь под него другой, как протез. Клиент счастлив, старый код не тронут, а ты — герой, который всё связал.
Так в чём, блядь, разница? Краткая шпаргалка:
| Что сравниваем | Decorator | Adapter |
|---|---|---|
| Зачем? | Чтобы улучшить и расширить объект новыми фичами. | Чтобы подогнать ебанутый интерфейс объекта под нужный. |
| Отношения | Декоратор и обёрнутый объект — братья, у них один интерфейс. | Адаптер и обёрнутый объект — чужие, у них разные интерфейсы. |
| Когда юзать? | Когда заранее продумываешь архитектуру, чтобы можно было гибко добавлять возможности. | Когда приспичило впихнуть чужой или старый код в свою систему, а переписывать его — овердохуя работы. |
| Что делает? | Добавляет поведение поверх существующего. | Переводит одно поведение в другое, как словарь. |
Проще говоря: Декоратор — это когда ты на базовую модель телефона клеишь защитное стекло, потом чехол, потом PopSocket. Адаптер — это когда у тебя зарядка от iPhone, а розетка в отеле — европейская, и ты суёшь между ними переходник. И да, оба паттерна используют композицию, но цели у них, ёпта, как небо и земля.