Шаблон разработки стратегии

Dec 01 2022
инкапсулировать то, что варьируется
Допустим, у нас есть приложение, позволяющее клиентам оплачивать покупку товара или получение услуги. Приложение имеет более одного способа оплаты, клиент может использовать наличные, карту Visa или кошелек, мы можем сделать это следующим образом: На самом деле нет, у нас есть три типа оплаты, и в каждом случае есть другая реализация, поэтому у нас есть одно поведение. «платить» разными способами, так что может случиться, если мы возьмем то, что различается, поместим в отдельные классы и будем использовать общий интерфейс для общего поведения.

Допустим, у нас есть приложение, позволяющее клиентам оплачивать покупку товара или получение услуги.

Приложение имеет более одного способа оплаты, клиент может использовать наличные деньги, карту Visa или кошелек, мы можем сделать это следующим образом:

public class Customer {

  public void pay(String paymentType) {
    if (StringUtils.isEmpty(paymentType)) {
      throw new RuntimeException("please add payment method");
    }
      // pay according to payment type
    if (paymentType.equalsIgnoreCase("cash")) {
      // pay in cash steps ( Algorithm )
    } else if (paymentType.equalsIgnoreCase("visa")) {
      // use the visa for payment steps ( Algorithm )
    } else if (paymentType.equalsIgnoreCase("wallet")) {
      // use the wallet for payment steps ( Algorithm )
    } else {
      throw new RuntimeException(paymentType + "payment type is not available");
    }
  }
}

public class Client {
    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.pay("cash");
    }
}

Вообще-то, нет,

  1. Если вы хотите добавить новый способ оплаты, то вам нужно изменить этот класс, и это делает ваш дизайн не гибким для любых новых изменений, этой реализацией мы нарушаем один из принципов дизайна SOLID, а именно: « программные объекты должны быть открытыми ». для расширения, но закрыт для модификации »
  2. Большая часть логики в классе не используется после того, как тип оплаты выбран клиентом, поэтому, если клиент хочет заплатить наличными, код, который обрабатывает визу и кошелек, не будет использоваться.
  3. Как вы видите в этом способе дизайна, вы должны обрабатывать каждый возможный ввод типа платежа, даже если он не имеет смысла, как тип платежа NULL или пустой тип платежа, и вы можете легко запретить клиенту выбирать только допустимые типы платежей. , используя шаблон стратегии.

У нас есть три типа оплаты, и в каждом случае есть своя реализация, поэтому у нас есть одно поведение, которое «оплачивает» разными способами, так что может случиться, если мы возьмем то, что различается, и поместим его в отдельные классы, и будем использовать общий интерфейс для общего поведения. .

Давайте создадим интерфейс под названием Payment . Он будет иметь абстрактный метод, и каждый отдельный метод оплаты имеет свой конкретный класс, который реализует интерфейс оплаты . Клиент будет иметь интерфейс оплаты в виде переменной «использовать композицию», а тип метода будет определяться по выбору клиента.

public interface Payment {
    void pay();
}
public class CashPayment implements Payment {
    @Override
    public void pay() {
        // pay in cash steps ( Algorithm )
    }
}
public class VisaPayment implements Payment {
    @Override
    public void pay() {
        // use the visa for payment steps ( Algorithm )
    }
}
public class WalletPayment implements Payment {
    @Override
    public void pay() {
        // use the wallet for payment steps ( Algorithm )
    }
}

public class Customer {
    private Payment payment;

    public Customer(Payment payment) {
        this.payment = payment;
    }

    public void setPaymentType(Payment payment) {
        this.payment = payment;
    }

    public void pay() {
        payment.pay();
    }
}

public class Client {
    public static void main(String[] args) {
        Customer customer = new Customer(new VisaPayment());
        customer.pay();
        // you can change the payment type at runtime
        customer.setPaymentType(new CashPayment());
        customer.pay();
    }
}

Диаграмма UML для примера платежа клиента

Принципы дизайна

из предыдущего обсуждения мы можем заключить четыре принципа проектирования:

  1. Дизайн для интерфейса, а не для реализации
  2. Инкапсулируйте то, что меняется
  3. Программные объекты должны быть открыты для расширения, но закрыты для модификации.
  4. Используйте композицию вместо наследования

Книжное определение шаблона стратегии звучит так: определите семейство алгоритмов, инкапсулируйте каждый из них и сделайте их взаимозаменяемыми. Стратегия позволяет алгоритму изменяться независимо от клиентов, которые его используют.

В нашем примере семейством алгоритмов были способы оплаты, и мы инкапсулировали каждый из них в конкретный подкласс, и они взаимозаменяемы клиентом, который может выбрать любой тип оплаты даже во время выполнения, алгоритм отделен от клиента и клиента. …. и это шаблон стратегии .