В нашей повседневной разработке мы часто сталкиваемся с подобными сценариями: когда нужно сделать что-то одно, шаги этого дела фиксированы, но конкретный способ реализации каждого шага не определен.
Обычно в этом случае мы абстрагируем все, что нужно сделать, в абстрактный класс и определяем метод шаблона в этом классе. Это называется шаблоном метода шаблона.
метод предыдущего шаблона
В моей предыдущей статье "Шаблоны проектирования — шаблонный подходВ статье приведен пример:
Когда мы идем в бизнес-зал банка для ведения бизнеса, нам необходимо выполнить следующие шаги: 1. Получить номер, 2. Занять дело и 3. Оценка.
В трех шагах выбор номера и оценка являются фиксированными процессами, и все должны делать одно и то же. Однако этап ведения бизнеса различается в зависимости от того, что каждый человек хочет делать, поэтому его нужно реализовывать по-разному.
Мы можем инкапсулировать весь бизнес в абстрактный класс:
/**
* 模板方法设计模式的抽象类
* @author hollis
*/
public abstract class AbstractBusinessHandler {
/**
* 模板方法
*/
public final void execute(){
getNumber();
handle();
judge();
}
/**
* 取号
* @return
*/
private void getNumber(){
System.out.println("number-00" + RandomUtils.nextInt());
}
/**
* 办理业务
*/
public abstract void handle(); //抽象的办理业务方法,由子类实现
/**
* 评价
*/
private void judge(){
System.out.println("give a praised");
}
}
Мы определяем класс выполнения в классе, который упорядочивает три метода: getNumber, handle и Judge. этоМетод шаблона.
Среди них getNumber и Judge имеют общие реализации, только метод handle является абстрактным и должен быть переписан подклассами в соответствии с реальным бизнес-контентом.
С этим абстрактным классом и методом шаблона, когда мы хотим реализовать «бизнес по внесению денег», нам нужно только наследовать AbstractBusinessHandler и переопределить метод дескриптора:
public class SaveMoneyHandler extends AbstractBusinessHandeler {
@Override
public void handle() {
System.out.println("save 1000");
}
}
Таким образом, когда мы выполняем бизнес-логику экономии денег, нам нужно только вызвать метод выполнения SaveMoneyHandler:
public static void main(String []args){
SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();
saveMoneyHandler.execute();
}
Выходной результат:
number-00958442164
save 1000
give a praised
Выше приведена реализация простого шаблонного метода. Используя методы шаблона, это может помочь нам повторно использовать код в значительной степени.
Поскольку нам приходится обрабатывать много операций в банке, нам может понадобиться определить множество классов реализации:
//取钱业务的实现类
public class DrawMoneyHandler extends AbstractBusinessHandeler {
@Override
public void handle() {
System.out.println("draw 1000");
}
}
//理财业务的实现类
public class MoneyManageHandler extends AbstractBusinessHandeler{
@Override
public void handle() {
System.out.println("money manage");
}
}
В течение долгого времени разработчики в основном использовали шаблонный метод, подобный приведенному выше примеру: вам нужно подготовить абстрактный класс, реализовать некоторую логику в виде конкретных методов и конкретных конструкторов, а затем объявить некоторые абстрактные методы, чтобы позволить подклассам реализовать оставшиеся. логика. Различные подклассы могут по-разному реализовывать эти абстрактные методы и, таким образом, иметь разные реализации оставшейся логики.
но,В Java 8 есть еще один способ реализации шаблонных методов, и нет необходимости определять слишком много классов реализации.
Функциональное программирование с Java 8
В 2014 году Oracle выпустила Java 8, и самой важной новой функцией Java 8 является поддержка функционального программирования.
Java 8 вjava.util.function
Следующее добавляет серию функциональных интерфейсов. Среди них Потребитель, Поставщик, Предикат, Функция и так далее.
В этой статье в основном мы хотим представить Поставщика и Потребителя, два пользовательских интерфейса, которые могут помочь нам очень хорошо преобразовать метод шаблона. Это всего лишь краткое введение в их использование, и мы не будем вдаваться в подробности. Если вы хотите узнать больше об использовании, вы можете сами погуглить.
Supplier
Поставщик – этотип питанияПроще говоря, интерфейс — это метод, который возвращает какое-то значение.
Самый простой Поставщик — это следующий код:
public List<String> getList() {
return new ArrayList();
}
Использование Поставщика выражается как:
Supplier<List<String>> listSupplier = ArrayList::new;
Consumer
Потребительский интерфейспотреблениеИнтерфейсы, просто, это метод определенного значения (например, параметры метода), который подвергается эксплуатации.
Самый простой Consumer — это следующий код:
public void sum(String a1) {
System.out.println(a1);
}
Использование представления Consumer:
Consumer<String> printConsumer = a1 -> System.out.println(a1);
Использование Consumer, наиболее распространенным примером являетсяStream.forEach(Consumer)
такое использование,
Он принимает Consumer, который потребляет элементы в итерируемом потоке, и выполняет некоторые действия с каждым элементом, например печать:
Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);
Шаблонные методы начиная с Java 8
После представления Consumer и Supplier в Java 8 давайте посмотрим, как преобразовать метод шаблона, который мы представили ранее.
Сначала мы определяем класс BankBusinessHandler и переопределяем метод execute. Этот метод имеет входной параметр типа Consumer, а затем удаляет метод дескриптора. Метод перестроенного шаблона выглядит следующим образом:
/**
* @author Hollis
*/
public class BankBusinessHandler {
private void execute(Consumer<BigDecimal> consumer) {
getNumber();
consumer.accept(null);
judge();
}
private void getNumber() {
System.out.println("number-00" + RandomUtils.nextInt());
}
private void judge() {
System.out.println("give a praised");
}
}
В реализованном нами шаблонном методе execute расположены getNumber, Judge и Consumer.accept, где Consumer.accept — это конкретная бизнес-логика, которая может заключаться в накоплении денег, снятии денег, финансировании и т.д. Его нужно передавать, когда другие методы вызывают execute.
В настоящее время, когда мы хотим реализовать бизнес по «сбережению денег», нам нужно добавить в класс BankBusinessHandler следующие методы:
/**
* @author Hollis
*/
public class BankBusinessHandler {
public void save(BigDecimal amount) {
execute(a -> System.out.println("save " + amount));
}
}
В методе save вызовите метод execute и передайте во входном параметре объект Comsumer, реализующий бизнес-логику «экономии денег».
Таким образом, когда мы выполняем бизнес-логику внесения денег, нам нужно только вызвать метод сохранения BankBusinessHandler:
public static void main(String[] args) throws {
BankBusinessHandler businessHandler = new BankBusinessHandler();
businessHandler.save(new BigDecimal("1000"));
}
Выходной результат:
number-001736151440
save1000
give a praised
Как и выше, когда мы хотим реализовать бизнес-логику, такую как снятие денег и управление финансами, это похоже на экономию денег:
/**
* @author Hollis
*/
public class BankBusinessHandler {
public void save(BigDecimal amount) {
execute(a -> System.out.println("save " + amount));
}
public void draw(BigDecimal amount) {
execute(a -> System.out.println("draw " + amount));
}
public void moneyManage(BigDecimal amount) {
execute(a -> System.out.println("draw " + amount));
}
}
Как видите, используя Comsumer в Java 8, мы преобразовали метод шаблона,После преобразования больше не требуются абстрактные классы и абстрактные методы, и нет необходимости создавать класс реализации для каждого бизнеса.. Мы можем объединить всю бизнес-логику в одном бизнес-классе. Это очень удобно для последующей работы и обслуживания этого кода.
Я рассказал, как использовать Consumer для преобразования метода шаблона, так в чем же польза от Supplier?
В нашем примере на трех этапах получения номера, ведения бизнеса и оценки бизнес необходимо настроить в соответствии с бизнес-ситуацией, поэтому в методе шаблона мы открываем бизнес как точку расширения наружу Мир.
Бывает такая ситуация, то есть когда мы сейчас занимаемся бизнесом, то способ получения номера разный, это может быть получение номера в отделении банка, получение номера онлайн, запись на расчетный счет в банке. менеджеру без необходимости получения номера.
Независимо от того, как был получен номер, конечным результатом является получение номера, и тип полученного номера может быть другим, и конкретные услуги могут быть разными.Например, номер VIP будет отправлен на стойку VIP для бизнеса, и Т. Д.
Для реализации такой бизнес-логики нужно использовать Supplier,Поставщик — это «поставщик», которого можно использовать для настройки «логики подсчета».
Во-первых, нам нужно преобразовать метод шаблона:
/**
* 模板方法
*/
protected void execute(Supplier<String> supplier, Consumer<BigDecimal> consumer) {
String number = supplier.get();
System.out.println(number);
if (number.startsWith("vip")) {
//Vip号分配到VIP柜台
System.out.println("Assign To Vip Counter");
}
else if (number.startsWith("reservation")) {
//预约号分配到专属客户经理
System.out.println("Assign To Exclusive Customer Manager");
}else{
//默认分配到普通柜台
System.out.println("Assign To Usual Manager");
}
consumer.accept(null);
judge();
}
После преобразования во входной параметр execute добавляется поставщик, и этот поставщик может указать номер. Что касается того, как получить номер, передайте его методу, который вызывает execute.
После этого мы можем определить несколько методов депозита, а именно VIP-депозит, резервационный депозит и обычный депозит:
public class BankBusinessHandler extends AbstractBusinessHandler {
public void saveVip(BigDecimal amount) {
execute(() -> "vipNumber-00" + RandomUtils.nextInt(), a -> System.out.println("save " + amount));
}
public void save(BigDecimal amount) {
execute(() -> "number-00" + RandomUtils.nextInt(), a -> System.out.println("save " + amount));
}
public void saveReservation(BigDecimal amount) {
execute(() -> "reservationNumber-00" + RandomUtils.nextInt(), a -> System.out.println("save " + amount));
}
}
В нескольких различных методах депозита реализуйте различную логику получения номеров, инкапсулируйте логику получения номеров в поставщике, а затем передайте ее в метод execute.
Код теста выглядит следующим образом:
BankBusinessHandler businessHandler = new BankBusinessHandler();
businessHandler.saveVip(new BigDecimal("1000"));
Выходной результат:
vipNumber-001638110566
Assign To Vip Counter
save 1000
give a praised
Выше мы преобразовали шаблон метода шаблона с помощью Comsumer и Supplier.
После использования Java 8 для преобразования метода шаблона объем кода может быть дополнительно уменьшен, по крайней мере, может быть создано меньшее количество классов реализации, что значительно сокращает повторяющийся код и повышает удобство сопровождения.
Конечно, этот подход не идеален, есть небольшой недостаток, т.Стоимость понимания немного выше, для тех разработчиков, которые не знакомы с функциональным программированием, стоимость начала работы немного выше. . .
Суммировать
Выше мы представили, что такое шаблон метода шаблона и как использовать Comsumer и Supplier для преобразования шаблона метода шаблона.
Такой подход часто используется в нашей повседневной разработке.На самом деле, я не думаю, что примеры в этой статье могут полностью выразить то, что я хочу выразить, но логика в нашем реальном бизнесе сложнее.
Поэтому это требует от каждого понимания и много практики. Если вы использовали шаблон метода шаблона в своем коде, его необходимо преобразовать с помощью методов, описанных в этой статье.
Если вы не использовали шаблон метода шаблона, это означает, что в вашем приложении должно быть много повторяющегося кода, поэтому поторопитесь и используйте его.
Как инженер-разработчик, мы должны сделать все возможное, чтобы исключить дублирование кода в приложении.
Об авторе:Hollis, человек с уникальным увлечением программированием, технический эксперт Alibaba, соавтор «Трех курсов для программистов» и автор серии статей «Дорога к Java-инженерам».
Обратите внимание на общедоступный номер【Hollis], фоновый ответ «Карта Бога» может получить расширенную карту разума инженеров Java.