Автор|Чжоу Ми (The Leaf)
Паттерн стратегии определяет набор стратегий, которые инкапсулированы в разные классы, и каждая стратегия может быть заменена друг другом в соответствии с текущим сценарием, так что изменение стратегии может быть независимым от оператора. Например, когда мы хотим куда-то поехать, мы выбираем разные способы передвижения (совместное использование велосипедов, поездка на автобусе, такси Диди и т. д.) в зависимости от расстояния (или в зависимости от текущей экономической ситуации). .
Когда использовать шаблон стратегии
Ali Development Protocol — Programming Protocol — Control Statement — Статья 6: Код логического суждения if-else с более чем 3 уровнями может быть реализован с использованием защитного оператора, режима стратегии, режима состояния и т. д. Я думаю, что все видели этот код:
if (conditionA) {
逻辑1
} else if (conditionB) {
逻辑2
} else if (conditionC) {
逻辑3
} else {
逻辑4
}
Хотя этот код прост в написании, он явно нарушает два основных принципа объектной ориентации:
-
Принцип единой ответственности (у класса должна быть только одна причина для изменения): поскольку любая логика изменяется позже, текущий класс будет изменен
-
Принцип открытости-закрытости (открыт для расширения, закрыт для модификации): если в это время необходимо добавить (удалить) логику, изменение исходного кода неизбежно.
Поскольку два вышеупомянутых принципа нарушаются, особенно когда объем кода в блоке if-else относительно велик, расширение и поддержка последующего кода постепенно становятся очень сложными и подверженными ошибкам, а использование защитных операторов не может избежать над двумя. проблема. Итак, основываясь на своем опыте, я придумал практику, которую лично считаю лучшей:
-
If-else не превышает 2-х слоев, код в блоке 1~5 строк, напрямую прописанный в блоке, в противном случае он инкапсулируется как метод
-
Если-else имеет более 2-х слоев, но код в блоке не превышает 3-х строк, попробуйте использовать сторожевые операторы
-
Если-иначе превышает 2 слоя, а код в блоке превышает 3 строки, попробуйте использовать режим стратегии
Получайте удовольствие, используя режим стратегии
В Spring существуют различные способы реализации шаблона стратегии.Позвольте мне поделиться моим текущим «лучшим способом» реализации шаблона стратегии (если у вас есть лучший способ, пожалуйста, просветите меня и обсудите его вместе).
фон спроса
Динамические формы для нашей платформы, ранее предназначенные для ввода данных модели. Теперь бизнес-сторона надеется открыть возможности формы, которые можно использовать не только для представления модели, но и для представления заданных функций бизнес-стороны (метод предназначен для привязки обобщенного сервиса HSF, HSF является Структура RPC внутри Департамента Дао). В дополнение к отправке в «режиме предварительного просмотра», когда мы настраиваем форму, форма в настоящее время имеет следующие три типа отправки:
-
Отправить во время предварительного просмотра формы
-
Фиксация при вводе модели
-
Зафиксировать при привязке HSF
А вот и мой «лучший распорядок дня».
Первым шагом является определение интерфейса политики.
Сначала определите интерфейс стратегии, включая два метода:
1. Как получить тип полиса
2. Методы обработки логики стратегии
/**
* 表单提交处理器
*/
public interface FormSubmitHandler<R extends Serializable> {
/**
* 获得提交类型(返回值也可以使用已经存在的枚举类)
*
* @return 提交类型
*/
String getSubmitType();
/**
* 处理表单提交请求
*
* @param request 请求
* @return 响应,left 为返回给前端的提示信息,right 为业务值
*/
CommonPairResponse<String, R> handleSubmit(FormSubmitRequest request);
}
/**
* 表单提交的请求
*/
@Getter
@Setter
public class FormSubmitRequest {
/**
* 提交类型
*
* @see FormSubmitHandler#getSubmitType()
*/
private String submitType;
/**
* 用户 id
*/
private Long userId;
/**
* 表单提交的值
*/
private Map<String, Object> formInput;
// 其他属性
}
Среди них метод getSubmitType класса FormSubmitHandler используется для получения типа отправки формы (то есть типа политики), который используется для непосредственного получения соответствующей реализации политики в соответствии с переданными клиентом параметрами; клиентом инкапсулируются как FormSubmitRequest и передаются в handleSubmit для обработки.
Второй шаг – реализация соответствующих стратегий.
Отправить во время предварительного просмотра формы
@Component
public class FormPreviewSubmitHandler implements FormSubmitHandler<Serializable> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public String getSubmitType() { return "preview"; }
@Override
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
logger.info("预览模式提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
return CommonPairResponse.success("预览模式提交数据成功!", null);
}
}
Фиксация при вводе модели
@Component
public class FormModelSubmitHandler implements FormSubmitHandler<Long> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public String getSubmitType() { return "model"; }
@Override
public CommonPairResponse<String, Long> handleSubmit(FormSubmitRequest request) {
logger.info("模型提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 模型创建成功后获得模型的 id
Long modelId = createModel(request);
return CommonPairResponse.success("模型提交成功!", modelId);
}
private Long createModel(FormSubmitRequest request) {
// 创建模型的逻辑
return 123L;
}
}
Подача схемы HSF
@Component
public class FormHsfSubmitHandler implements FormSubmitHandler<Serializable> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public String getSubmitType() { return "hsf"; }
@Override
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
logger.info("HSF 模式提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 进行 HSF 泛化调用,获得业务方返回的提示信息和业务数据
CommonPairResponse<String, Serializable> response = hsfSubmitData(request);
return response;
}
...
}
Шаг 3. Создайте простую фабрику стратегии
@Component
public class FormSubmitHandlerFactory implements InitializingBean, ApplicationContextAware {
private static final
Map<String, FormSubmitHandler<Serializable>> FORM_SUBMIT_HANDLER_MAP = new HashMap<>(8);
private ApplicationContext appContext;
/**
* 根据提交类型获取对应的处理器
*
* @param submitType 提交类型
* @return 提交类型对应的处理器
*/
public FormSubmitHandler<Serializable> getHandler(String submitType) {
return FORM_SUBMIT_HANDLER_MAP.get(submitType);
}
@Override
public void afterPropertiesSet() {
// 将 Spring 容器中所有的 FormSubmitHandler 注册到 FORM_SUBMIT_HANDLER_MAP
appContext.getBeansOfType(FormSubmitHandler.class)
.values()
.forEach(handler -> FORM_SUBMIT_HANDLER_MAP.put(handler.getSubmitType(), handler));
}
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
appContext = applicationContext;
}
}
Мы позволяем FormSubmitHandlerFactory реализовать интерфейс InitializingBean.В методе afterPropertiesSet все FormSubmitHandler автоматически регистрируются в FORM_SUBMIT_HANDLER_MAP на основе контейнера Spring, поэтому после запуска контейнера Spring метод getHandler может напрямую получить соответствующий обработчик отправки формы через submitType.
Шаг 4, используйте и тестируйте
В службе форм мы получаем соответствующий обработчик отправки формы через FormSubmitHandlerFactory для обработки различных типов отправки:
@Service
public class FormServiceImpl implements FormService {
@Autowired
private FormSubmitHandlerFactory submitHandlerFactory;
public CommonPairResponse<String, Serializable> submitForm(@NonNull FormSubmitRequest request) {
String submitType = request.getSubmitType();
// 根据 submitType 找到对应的提交处理器
FormSubmitHandler<Serializable> submitHandler = submitHandlerFactory.getHandler(submitType);
// 判断 submitType 对应的 handler 是否存在
if (submitHandler == null) {
return CommonPairResponse.failure("非法的提交类型: " + submitType);
}
// 处理提交
return submitHandler.handleSubmit(request);
}
}
Фабрика отвечает только за получение обработчиков, обработчики отвечают только за обработку конкретных представлений, а служба отвечает только за логическую организацию, чтобы достичь функциональной «низкой связанности и высокой связности».
Напишите простой контроллер:
@RestController
public class SimpleController {
@Autowired
private FormService formService;
@PostMapping("/form/submit")
public CommonPairResponse<String, Serializable> submitForm(@RequestParam String submitType,
@RequestParam String formInputJson) {
JSONObject formInput = JSON.parseObject(formInputJson);
FormSubmitRequest request = new FormSubmitRequest();
request.setUserId(123456L);
request.setSubmitType(submitType);
request.setFormInput(formInput);
return formService.submitForm(request);
}
}
Наконец, простой тест:
Я чувствую это, это очень приятное ощущение~
Представьте себе расширение
Если нам нужно добавить новую стратегию, например привязать отправку функции FaaS, нам просто нужно добавить новую реализацию стратегии:
@Component
public class FormFaasSubmitHandler implements FormSubmitHandler<Serializable> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public String getSubmitType() { return "faas"; }
@Override
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
logger.info("FaaS 模式的提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 进行 FaaS 函数调用,并获得业务方返回的提示信息和业务数据
CommonPairResponse<String, Serializable> response = faasSubmitData(request);
return response;
}
...
}
В настоящее время нет необходимости изменять какой-либо код, потому что FormFaasSubmitHandler будет автоматически зарегистрирован в FormSubmitHandlerFactory при перезапуске контейнера Spring — программирование, ориентированное на Spring, слишком мило~