Шаблон стратегии определяет семейство алгоритмов, инкапсулированных в класс драйвера, обычно известный как Context, и обеспечивает взаимозаменяемость алгоритмов. Это делает алгоритмы легко взаимозаменяемыми и предоставляет механизм для выбора подходящего алгоритма в определенное время.
Алгоритмы (стратегии) выбираются во время выполнения клиентом или контекстом. Класс Context обрабатывает все данные во время взаимодействия с клиентом.
Основные участники модели Стратегии представлены ниже:
- Стратегия — Определяет интерфейс для всех алгоритмов. Этот интерфейс используется для вызова алгоритмов, определенных ConcreteStrategy.
- Контекст — содержит ссылку на объект Стратегия.
- ConcreteStrategy — Фактическая реализация алгоритма согласно интерфейсу Стратегии
Теперь давайте рассмотрим конкретный пример шаблона стратегии и посмотрим, как он преобразуется с помощью лямбда-выражений. Предположим, у нас есть разные типы ставок для расчета подоходного налога. В зависимости от того, уплачен ли налог заранее или поздно, существует скидка или штраф соответственно. Мы можем инкапсулировать эту функциональность в одном классе с разными методами, но для этого потребуется модификация класса, если в будущем потребуется какой-то другой расчет налога. Это не эффективный подход. Изменения в реализации класса должны быть последним средством.
Давайте выберем оптимальный подход, используя шаблон стратегии. Мы сделаем интерфейс для налоговой стратегии с помощью основного метода:
1
2
3
4
|
public interface TaxStrategy { public double calculateTax( double income); } |
Теперь давайте определимся с конкретной стратегией нормального подоходного налога.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class PersonalTaxStrategy implements TaxStrategy { public PersonalTaxStrategy() { } @Override public double calculateTax( double income) { System.out.println( "PersonalTax" ); double tax = income * 0.3 ; return tax; } } |
Класс PersonalTaxStrategy соответствует интерфейсу TaxStrategy. Аналогично, давайте определим конкретную стратегию для просрочки уплаты налога, которая влечет за собой штраф.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class PersonalTaxPenaltyStrategy implements TaxStrategy { public PersonalTaxPenaltyStrategy() { } @Override public double calculateTax( double income) { System.out.println( "PersonalTaxWithPenalty" ); double tax = income * 0.4 ; return tax; } } |
Далее давайте определим конкретную стратегию для уплаты авансового налога, которая приведет к возврату налога.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class PersonalTaxRebateStrategy implements TaxStrategy { public PersonalTaxRebateStrategy() { } @Override public double calculateTax( double income) { System.out.println( "PersonalTaxWithRebate" ); double tax = income * 0.2 ; return tax; } } |
Теперь давайте объединим все классы и интерфейсы, определенные, чтобы использовать возможности паттерна Стратегия. Пусть основной метод выступает в качестве контекста для различных стратегий. Посмотрите только один пример взаимодействия всех этих классов:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
import java.util.Arrays; import java.util.List; public class TaxStrategyMain { public static void main(String [] args) { //Create a List of Tax strategies for different scenarios List<TaxStrategy> taxStrategyList = Arrays.asList( new PersonalTaxStrategy(), new PersonalTaxPenaltyStrategy(), new PersonalTaxRebateStrategy()); //Calculate Tax for different scenarios with corresponding strategies for (TaxStrategy taxStrategy : taxStrategyList) { System.out.println(taxStrategy.calculateTax( 30000.0 )); } } } |
Запуск этого дает следующий вывод:
1
|
PersonalTax |
1
|
9000.0 |
1
|
PersonalTaxWithPenalty |
1
|
12000.0 |
1
|
PersonalTaxWithRebate |
1
|
6000.0 |
Это ясно демонстрирует, как различные налоговые ставки могут быть рассчитаны с использованием соответствующего класса конкретной стратегии. Я попытался объединить все конкретные стратегии (алгоритмы) в списке, а затем получить к ним доступ, перебирая список.
То, что мы видели до сих пор, это просто стандартный шаблон стратегии, и он существует уже давно. В наше время, когда функциональное программирование является новым модным словом, можно подумать о поддержке лямбда-выражений в Java, можно ли сделать что-то по-другому? Действительно, поскольку интерфейс стратегии похож на функциональный интерфейс, мы можем перефразировать, используя лямбда-выражения в Java. Давайте посмотрим, как выглядит код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
import java.util.Arrays; import java.util.List; public class TaxStrategyMainWithLambda { public static void main(String [] args) { //Create a List of Tax strategies for different scenarios with inline logic using Lambda List<TaxStrategy> taxStrategyList = Arrays.asList( (income) -> { System.out.println( "PersonalTax" ); return 0.30 * income; }, (income) -> { System.out.println( "PersonalTaxWithPenalty" ); return 0.40 * income; }, (income) -> { System.out.println( "PersonalTaxWithRebate" ); return 0.20 * income; } ); //Calculate Tax for different scenarios with corresponding strategies taxStrategyList.forEach((strategy) -> System.out.println(strategy.calculateTax( 30000.0 ))); } } |
Запуск этого дает похожий вывод:
1
|
PersonalTax |
1
|
9000.0 |
1
|
PersonalTaxWithPenalty |
1
|
12000.0 |
1
|
PersonalTaxWithRebate |
1
|
6000.0 |
Мы видим, что использование лямбда-выражений делает ненужными дополнительные классы для конкретных стратегий. Вам не нужны дополнительные занятия; просто укажите дополнительное поведение с помощью лямбда-выражения.
- Все фрагменты кода доступны из моего репозитория github.
Ссылка: | Java 8 лямбда-выражение для шаблонов проектирования — шаблон разработки стратегии от нашего партнера по JCG Гурприта Сачдева в блоге gssachdeva . |