Адаптер (Adapter)

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

Когда надо использовать Адаптер?

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

Формальное определение паттерна на UML выглядит следующим образом:

Паттерн адаптер объектов в C#

Формальное описание адаптера объектов на C# выглядит таким образом:

class Client
{
    public void Request(Target target)
    {
        target.Request();
    }
}
// класс, к которому надо адаптировать другой класс   
class Target
{
    public virtual void Request()
    {}
}
  
// Адаптер
class Adapter : Target
{
    private Adaptee adaptee = new Adaptee();
  
    public override void Request()
    {
        adaptee.SpecificRequest();
    }
}
  
// Адаптируемый класс
class Adaptee
{
    public void SpecificRequest()
    {}
}

Участники

  • Target: представляет объекты, которые используются клиентом
  • Client: использует объекты Target для реализации своих задач
  • Adaptee: представляет адаптируемый класс, который мы хотели бы использовать у клиента вместо объектов Target
  • Adapter: собственно адаптер, который позволяет работать с объектами Adaptee как с объектами Target.

То есть клиент ничего не знает об Adaptee, он знает и использует только объекты Target. И благодаря адаптеру мы можем на клиенте использовать объекты Adaptee как Target

Теперь разберем реальный пример. Допустим, у нас есть путешественник, который путешествует на машине. Но в какой-то момент ему приходится передвигаться по пескам пустыни, где он не может ехать на машине. Зато он может использовать для передвижения верблюда. Однако в классе путешественника использование класса верблюда не предусмотрено, поэтому нам надо использовать адаптер:

class Program
{
    static void Main(string[] args)
    {
        // путешественник
        Driver driver = new Driver();
        // машина
        Auto auto = new Auto();
        // отправляемся в путешествие
        driver.Travel(auto);
        // встретились пески, надо использовать верблюда
        Camel camel = new Camel();
        // используем адаптер
        ITransport camelTransport = new CamelToTransportAdapter(camel);
        // продолжаем путь по пескам пустыни
        driver.Travel(camelTransport);
 
        Console.Read();
    }
}
interface ITransport
{
    void Drive();
}
// класс машины
class Auto : ITransport
{
    public void Drive()
    {
        Console.WriteLine("Машина едет по дороге");
    }
}
class Driver
{
    public void Travel(ITransport transport)
    {
        transport.Drive();
    }
}
// интерфейс животного
interface IAnimal
{
    void Move();
}
// класс верблюда
class Camel : IAnimal
{
    public void Move()
    {
        Console.WriteLine("Верблюд идет по пескам пустыни");
    }
}
// Адаптер от Camel к ITransport
class CamelToTransportAdapter : ITransport
{
    Camel camel;
    public CamelToTransportAdapter(Camel c)
    {
        camel = c;
    }
 
    public void Drive()
    {
        camel.Move();
    }
}

И консоль выведет:

Машина едет по дороге
Верблюд идет по пескам пустыни

В данном случае в качестве клиента применяется класс Driver, который использует объект ITransport. Адаптируемым является класс верблюда Camel, который нужно использовать в качестве объекта ITransport. И адптером служит класс CamelToTransportAdapter.

Возможно, кому-то покажется надуманной проблема использования адаптеров особенно в данном случае, так как мы могли бы применить интерфейс ITransport к классу Camel и реализовать его метод Drive(). Однако, в данном случае может случиться дублирование функциональностей: интерфейс IAnimal имеет метод Move(), реализация которого в классе верблюда могла бы быть похожей на реализацию метода Drive() из интерфейса ITransport. Кроме того, нередко бывает, что классы спроектированы кем-то другим, и мы никак не можем на них повлиять. Мы только используем их. В результате чего адаптеры довольно широко распространены в .NET. В частности, многочисленные встроенные классы, которые используются для подключения к различным системам баз данных, как раз и реализуют паттерн адаптер (например, класс System.Data.SqlClient.SqlDataAdapter).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *