Adapter (Адаптер)

Паттерн Adapter (Адаптер) позволяет объектам с несовместимыми интерфейсами работать вместе. Он выступает в роли промежуточного слоя между двумя классами, превращая интерфейс одного класса в интерфейс, ожидаемый другим.

Когда применять Adapter

  • Когда нужно использовать существующий класс, но его интерфейс не соответствует требуемому.
  • Когда необходимо интегрировать сторонние библиотеки без изменения их исходного кода.
  • Когда в проекте присутствуют классы с разными интерфейсами, но схожим функционалом.

PlantUML-диаграмма

UML диаграмма Adapter

// Целевой интерфейс — то, что ожидает клиент
public interface ITarget
{
    string GetRequest();
}

// Существующий несовместимый класс
public class Adaptee
{
    public string GetSpecificRequest()
    {
        return "Запрос от несовместимого класса (Adaptee)";
    }
}

// Адаптер — преобразует интерфейс Adaptee к интерфейсу ITarget
public class Adapter : ITarget
{
    private readonly Adaptee _adaptee;

    public Adapter(Adaptee adaptee)
    {
        _adaptee = adaptee;
    }

    public string GetRequest()
    {
        return $"[Адаптировано]: {_adaptee.GetSpecificRequest()}";
    }
}

// Клиентский код
class Program
{
    static void Main()
    {
        Adaptee adaptee = new Adaptee();
        ITarget target = new Adapter(adaptee);

        Console.WriteLine(target.GetRequest());
    }
}
// Целевой интерфейс — то, что ожидает клиент
type Target interface {
	Request() string
}

// Существующий несовместимый тип
type Adaptee struct{}

func (Adaptee) SpecificRequest() string {
	return "Запрос от несовместимого типа (Adaptee)"
}

// Адаптер — приводит Adaptee к интерфейсу Target
type Adapter struct {
	adaptee Adaptee
}

func (a Adapter) Request() string {
	return "[Адаптировано]: " + a.adaptee.SpecificRequest()
}

func main() {
	var target Target = Adapter{adaptee: Adaptee{}}

	fmt.Println(target.Request())
}
from typing import Protocol


class Target(Protocol):
    def request(self) -> str: ...


class Adaptee:
    def specific_request(self) -> str:
        return "Запрос от несовместимого класса (Adaptee)"


class Adapter:
    def __init__(self, adaptee: Adaptee) -> None:
        self._adaptee = adaptee

    def request(self) -> str:
        return f"[Адаптировано]: {self._adaptee.specific_request()}"


target: Target = Adapter(Adaptee())
print(target.request())
interface Target {
    getRequest(): string;
}

class Adaptee {
    getSpecificRequest(): string {
        return "Запрос от несовместимого класса (Adaptee)";
    }
}

class Adapter implements Target {
    constructor(private readonly adaptee: Adaptee) {}

    getRequest(): string {
        return `[Адаптировано]: ${this.adaptee.getSpecificRequest()}`;
    }
}

const target: Target = new Adapter(new Adaptee());
console.log(target.getRequest());
interface Target {
    String getRequest();
}

class Adaptee {
    String getSpecificRequest() {
        return "Запрос от несовместимого класса (Adaptee)";
    }
}

class Adapter implements Target {
    private final Adaptee adaptee;

    Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    public String getRequest() {
        return "[Адаптировано]: " + adaptee.getSpecificRequest();
    }
}

public class Program {
    public static void main(String[] args) {
        Target target = new Adapter(new Adaptee());

        System.out.println(target.getRequest());
    }
}
interface Target {
    fun getRequest(): String
}

class Adaptee {
    fun getSpecificRequest(): String = "Запрос от несовместимого класса (Adaptee)"
}

class Adapter(private val adaptee: Adaptee) : Target {
    override fun getRequest(): String = "[Адаптировано]: ${adaptee.getSpecificRequest()}"
}

fun main() {
    val target: Target = Adapter(Adaptee())

    println(target.getRequest())
}

Плюсы и минусы

Плюсы Минусы
Позволяет повторно использовать существующий код без изменения Увеличивает количество классов и усложняет структуру
Изолирует клиентский код от изменений в сторонних библиотеках Может привести к снижению производительности при сложных преобразованиях
Следует принципу открытости/закрытости (OCP) Иногда приводит к дублированию логики адаптации

Заключение

Adapter — это “переводчик” между несовместимыми интерфейсами. Он делает возможным совместную работу старого и нового кода, сохраняя гибкость и изоляцию компонентов.