Эта статья представляет собой заметку для чтения «Java 8 Combat», на чтение которой уходит около 5 минут.
Это немного захватывает заголовки, но именно так я в последнее время использую лямбда-выражения. Шаблоны проектирования — это сводка некоторых удачных опытов и подпрограмм в прошлом, но хорошие возможности языка могут заставить разработчиков не учитывать эти шаблоны проектирования. Общие шаблоны объектно-ориентированного проектирования включают шаблон стратегии, метод шаблона, шаблон наблюдателя, шаблон цепочки ответственности и шаблон фабрики Использование лямбда-выражений (мышление функционального программирования) помогает избежать этих фиксированных кодов в объектно-ориентированной разработке. Ниже мы выбрали для анализа два случая: паттерн стратегии и паттерн цепочки ответственности.
Случай 1: шаблон стратегии
Когда у нас есть разные решения проблемы, и мы не хотим, чтобы клиенты воспринимали детали этих решений, в этом случае подходит шаблон стратегии. Шаблон стратегии состоит из трех частей:
- Решить задачу алгоритма (Стратегия на рисунке выше);
- Одна или несколько конкретных реализаций таких алгоритмов (ConcreteStrategyA, ConcreteStrategyB и ConcreteStrategyC на рисунке выше)
- Один или несколько сценариев использования клиента (ClientContext на изображении выше)
объектно-ориентированный подход
Сначала определите интерфейс стратегии, который представляет стратегию сортировки:
public interface ValidationStrategy {
boolean execute(String s);
}
Затем определите конкретные классы реализации (т.е. разные алгоритмы сортировки):
public class IsAllLowerCase implements ValidationStrategy {
@Override
public boolean execute(String s) {
return s.matches("[a-z]+");
}
}
public class IsNumberic implements ValidationStrategy {
@Override
public boolean execute(String s) {
return s.matches("\\d+");
}
}
Наконец, определите сценарий использования клиента, код показан на следующем рисунке. Валидатор — это контекст, используемый для предоставления услуг клиентам.Каждый объект Валидатора инкапсулирует определенный объект Стратегии.В реальной работе мы можем обновить обслуживание клиентов, заменив определенные объекты Стратегии, не требуя от клиентов обновления.
public class Validator {
private final ValidationStrategy strategy;
public Validator(ValidationStrategy strategy) {
this.strategy = strategy;
}
/**
* 给客户的接口
*/
public boolean validate(String s) {
return strategy.execute(s);
}
}
public class ClientTestDrive {
public static void main(String[] args) {
Validator numbericValidator = new Validator(new IsNumberic());
boolean res1 = numbericValidator.validate("7780");
System.out.println(res1);
Validator lowerCaseValidator = new Validator(new IsAllLowerCase());
boolean res2 = lowerCaseValidator.validate("aaaddd");
System.out.println(res2);
}
}
идеи функционального программирования
Если вы используете лямбда-выражения для рассмотрения, вы обнаружите, что ValidationStrategy является функциональным интерфейсом (также имеет то же описание функции, что и Predicate), тогда вам не нужно определять вышеуказанные классы реализации, вы можете напрямую заменить их следующим кодом , причина в лямбда-выражении. Эти классы были инкапсулированы внутри.
public class ClientTestDrive {
public static void main(String[] args) {
Validator numbericValidator = new Validator((String s) -> s.matches("\\d+"));
boolean res1 = numbericValidator.validate("7789");
System.out.println(res1);
Validator lowerCaseValidator = new Validator((String s) -> s.matches("[a-z]+"));
boolean res2 = lowerCaseValidator.validate("aaaddd");
System.out.println(res2);
}
}
Пример 2: Модель цепочки ответственности
В некоторых сценариях необходимо выполнить ряд работ над объектом, и эти задачи выполняются разными классами, в этом случае удобнее использовать паттерн цепочки ответственности. Основные компоненты шаблона цепочки ответственности включают три:
- Абстрактный класс, управляющий последовательностью операций, в котором будет объект, записывающий последующие операции объектов текущего объекта;
- Некоторые конкретные объекты операций, эти объекты операций будут организованы в виде связанного списка
- Клиентский компонент, использующий этот шаблон, должен иметь дело только с одним компонентом и не должен быть связан со многими объектами операций.
объектно-ориентированный подход
Во-первых, давайте посмотрим на абстрактный класс ProcessingObject, который мы определили здесь, в котором поле преемника используется для управления последующими объектами операций объекта; интерфейс дескриптора используется в качестве интерфейса для предоставления услуг внешнему миру; используется handleWork как метод работы фактического объекта обработки.
public abstract class ProcessingObject<T> {
protected ProcessingObject<T> successor;
public void setSuccessor(ProcessingObject<T> successor) {
this.successor = successor;
}
public T handler(T input) {
T r = handleWork(input);
if (successor != null) {
return successor.handler(r);
}
return r;
}
abstract protected T handleWork(T input);
}
Затем можно определить два конкретных объекта операции, как показано в следующем коде. PS: Неуместно использовать метод replaceAll в книге "Java 8 Actual Combat". Для этого вы можете обратиться к нашей предыдущей статье -020: Дайте несколько String API и кейсов.
public class HeaderTextProcessing extends ProcessingObject<String> {
@Override
protected String handleWork(String input) {
return "From Raoul, Mario and Alan: " + input;
}
}
public class SpellCheckerProcessing extends ProcessingObject<String> {
@Override
protected String handleWork(String input) {
return input.replace("labda", "lambda");
}
}
Наконец, вы можете объединить два указанных выше конкретных объекта класса операций в последовательность операций в клиенте, см. следующий код:
public class Client {
public static void main(String[] args) {
ProcessingObject<String> p1 = new HeaderTextProcessing();
ProcessingObject<String> p2 = new SpellCheckerProcessing();
p1.setSuccessor(p2);
String result = p1.handler("Aren't labdas really sexy?!!");
System.out.println(result);
}
}
идеи функционального программирования
Если вы используете мышление функционального программирования, то схема цепочки ответственности проста — два метода y=f(x) и z=g(x) имеют дело с x, тогда, если эти две функции объединить вместе, ситуация r=f(g(x)) будет сформировано, то есть несколько процедур обработки можно соединить последовательно с помощью addThen в лямбда-выражении.
public class ClientWithLambda {
public static void main(String[] args) {
UnaryOperator<String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;
UnaryOperator<String> spellCheckProcessing = (String text) -> text.replace("labda", "lambda");
Function<String, String> function = headerProcessing.andThen(spellCheckProcessing);
String result = function.apply("Aren't labdas really sexy?!!");
System.out.println(result);
UnaryOperator<String> hhhhhProcessing = (String text) -> text.concat("hhhh");
Function<String, String> function1 = function.andThen(hhhhhProcessing);
String result1 = function1.apply("Aren't labdas really sexy?!!");
System.out.println(result1);
}
}
Выше приведен шаблон цепочки ответственности, реализованный с использованием собственного лямбда-выражения Java, мы также можем использовать предыдущую статью —vavr: позволяет писать на Java как на ScalaДля реализации библиотеки vavr, представленной в , код выглядит следующим образом:
public class ClientWithVavr {
public static void main(String[] args) {
Function1<String, String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;
Function1<String, String> specllCheckProcessing = (String text) -> text.replace("labda", "lambda");
Function1<String, String> function = headerProcessing.compose(specllCheckProcessing);
String result = function.apply("Aren't labdas really sexy?!!");
System.out.println(result);
}
}
Суммировать
Видно, что мышление функционального программирования отличается от мышления объектно-ориентированного программирования, и его выразительность сильнее.Поэтому как разработчику пора серьезно изучить мышление функционального программирования.Как Java-разработчик, я собираюсь начать с Начните с лямбда-выражений, затем попробуйте изучить возможности функционального программирования на Scala или Kotlin.
использованная литература
- "Практика программирования на Java"
- Дзен шаблонов проектирования
В этом выпуске основное внимание уделяется таким темам, как серверные технологии, устранение неполадок и оптимизация JVM, вопросы на собеседованиях по Java, личностный рост и самоуправление, а читателям предлагается опыт работы и роста передовых разработчиков. .