Compare commits

...

11 Commits

Author SHA1 Message Date
fujiayang
25e546e024 spring增强抽象工厂,继承改组合优化 2022-04-08 14:28:53 +08:00
fujiayang
d10845eb7e spring增强抽象工厂,反射优化 2022-04-08 14:23:28 +08:00
fujiayang
a6c22cd132 spring增强抽象工厂 2022-04-08 14:21:13 +08:00
fujiayang
d81b7beb05 spring增强访问者模式-改造后-jdk17 Pattern Matching 2022-01-26 11:30:23 +08:00
fujiayang
3f52da2575 spring增强访问者模式-改造后 2022-01-26 10:37:13 +08:00
fujiayang
86ce1f5d80 spring增强访问者模式-改造前(early binding)解决 2022-01-26 10:30:30 +08:00
fujiayang
3cbc35e558 spring增强访问者模式-改造前(early binding) 2022-01-26 10:29:56 +08:00
fujiayang
89404411f1 spring增强策略模式-改造后-需求变更扩展 2022-01-26 10:16:15 +08:00
fujiayang
f226ad20d1 spring增强策略模式-改造后 2022-01-26 10:01:33 +08:00
fujiayang
9347b5f4e4 spring增强策略模式-原始 2022-01-26 09:48:45 +08:00
e20286c47d Spring策略模式 2021-12-27 21:55:03 +08:00
47 changed files with 1487 additions and 8 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.idea
.classpath
.project
*.iml
target/
.DS_Store
.gitattributes
log/

View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />

1
.idea/compiler.xml generated
View File

@@ -2,6 +2,7 @@
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />

View File

@@ -6,6 +6,11 @@
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />

2
.idea/misc.xml generated
View File

@@ -11,7 +11,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

11
pom.xml
View File

@@ -12,8 +12,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<org.springframework.version>5.1.0.RELEASE</org.springframework.version>
</properties>
@@ -148,6 +148,13 @@
<version>8.0.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,12 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
/**
* @author F嘉阳
* @date 2021-12-27 21:37
*/
public class CommonPairResponse<T, R> {
public static <T, R> CommonPairResponse<T, R> success(String s, Object o) {
return null;
}
}

View File

@@ -0,0 +1,45 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* HSF 模式的提交
*
* @author F嘉阳
* @date 2021-12-27 21:43
*/
@Slf4j
@Component
public class FormHsfSubmitHandler implements FormSubmitHandler<Serializable> {
/**
* 获得提交类型(返回值也可以使用已经存在的枚举类)
*
* @return 提交类型
*/
@Override
public String getSubmitType() {
return "hsf";
}
/**
* 处理表单提交请求
*
* @param request 请求
* @return 响应left 为返回给前端的提示信息right 为业务值
*/
@Override
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
log.info("HSF 模式提交userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 进行 HSF 泛化调用,获得业务方返回的提示信息和业务数据
CommonPairResponse<String, Serializable> response = hsfSubmitData(request);
return response;
}
private CommonPairResponse<String, Serializable> hsfSubmitData(FormSubmitRequest request) {
return null;
}
}

View File

@@ -0,0 +1,43 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 模型输入时的提交
*
* @author F嘉阳
* @date 2021-12-27 21:40
*/
@Slf4j
@Component
public class FormModelSubmitHandler implements FormSubmitHandler<Long> {
/**
* 获得提交类型(返回值也可以使用已经存在的枚举类)
*
* @return 提交类型
*/
@Override
public String getSubmitType() {
return "model";
}
/**
* 处理表单提交请求
*
* @param request 请求
* @return 响应left 为返回给前端的提示信息right 为业务值
*/
@Override
public CommonPairResponse<String, Long> handleSubmit(FormSubmitRequest request) {
log.info("模型提交userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 模型创建成功后获得模型的 id
Long modelId = createModel(request);
return CommonPairResponse.success("模型提交成功!", modelId);
}
private Long createModel(FormSubmitRequest request) {
// 创建模型的逻辑
return 123L;
}
}

View File

@@ -0,0 +1,39 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* 预览表单时的提交
*
* @author F嘉阳
* @date 2021-12-27 21:39
*/
@Slf4j
@Component
public class FormPreviewSubmitHandler implements FormSubmitHandler<Serializable> {
/**
* 获得提交类型(返回值也可以使用已经存在的枚举类)
*
* @return 提交类型
*/
@Override
public String getSubmitType() {
return "preview";
}
/**
* 处理表单提交请求
*
* @param request 请求
* @return 响应left 为返回给前端的提示信息right 为业务值
*/
@Override
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
log.info("预览模式提交userId={}, formInput={}", request.getUserId(), request.getFormInput());
return CommonPairResponse.success("预览模式提交数据成功!", null);
}
}

View File

@@ -0,0 +1,26 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import java.io.Serializable;
/**
* 表单提交处理器
*
* @author F嘉阳
* @date 2021-12-27 21:36
*/
public interface FormSubmitHandler<R extends Serializable> {
/**
* 获得提交类型(返回值也可以使用已经存在的枚举类)
*
* @return 提交类型
*/
String getSubmitType();
/**
* 处理表单提交请求
*
* @param request 请求
* @return 响应left 为返回给前端的提示信息right 为业务值
*/
CommonPairResponse<String, R> handleSubmit(FormSubmitRequest request);
}

View File

@@ -0,0 +1,57 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* 策略的简单工厂
* 让 FormSubmitHandlerFactory 实现 InitializingBean 接口,在 afterPropertiesSet
* 方法中,基于 Spring 容器将所有 FormSubmitHandler 自动注册到 FORM_SUBMIT_HANDLER_MAP
* 从而 Spring 容器启动完成后, getHandler 方法可以直接通过 submitType
* 来获取对应的表单提交处理器
* <p>
* Factory 只负责获取 HandlerHandler 只负责处理具体的提交Service 只负责逻辑编排,
* 从而达到功能上的 “低耦合高内聚”。
*
* @author F嘉阳
* @date 2021-12-27 21:44
*/
@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;
}
}

View File

@@ -0,0 +1,39 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
/**
* 表单提交的请求
* FormSubmitHandler 的 getSubmitType 方法用来获取表单的提交类型(即策略类
* 型),用于根据客户端传递的参数直接获取到对应的策略实现;客户端传递的相关参数都被
* 封装为 FormSubmitRequest传递给 handleSubmit 进行处理。
*
* @author F嘉阳
* @date 2021-12-27 21:37
*/
@Getter
@Setter
public class FormSubmitRequest {
/**
* 提交类型
*
* @see FormSubmitHandler#getSubmitType()
*/
private String submitType;
/**
* 用户 id
*/
private Long userId;
/**
* 表单提交的值
*/
private Map<String, Object> formInput;
// 其他属性
}

View File

@@ -0,0 +1,109 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.apache.commons.lang3.StringUtils;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.*;
import java.util.ArrayList;
import java.util.List;
/**
* 表单项转换的操作流程,即如下的 convert 方法(使用 final 修饰,确保子类不可修改操作流程)
*
* @author F嘉阳
* @date 2022/1/26 09:50
*/
public abstract class AbstractFormItemConverter {
/**
* 子类可处理的表单项类型
*/
public abstract ComponentTypeEnum getType();
/**
* 将输入的配置转变为表单项的操作流程
*
* @param config 前端输入的配置
* @return 表单项
*/
public final FormItem convert(FormItemConfig config) {
FormItem item = createItem(config);
// 表单项创建完成之后,子类如果需要特殊处理,可覆写该方法
afterItemCreate(item, config);
FormComponentProps props = createComponentProps(config);
item.setComponentProps(props);
// 组件属性创建完成之后,子类如果需要特殊处理,可覆写该方法
afterPropsCreate(props, config);
List<FormItemRule> rules = createRules(config);
item.setRules(rules);
// 约束规则创建完成之后,子类如果需要特殊处理,可覆写该方法
afterRulesCreate(rules, config);
return item;
}
/**
* 共用逻辑:创建表单项、设置通用的表单项属性
*/
private FormItem createItem(FormItemConfig config) {
FormItem formItem = new FormItem();
formItem.setCode(config.getCode());
formItem.setTitle(config.getTitle());
formItem.setComponent(config.getComponent());
return formItem;
}
/**
* 表单项创建完成之后,子类如果需要特殊处理,可覆写该方法
*/
protected void afterItemCreate(FormItem item, FormItemConfig config) {
}
/**
* 共用逻辑:创建组件属性、设置通用的组件属性
*/
private FormComponentProps createComponentProps(FormItemConfig config) {
FormComponentProps props = new FormComponentProps();
if (config.isReadOnly()) {
props.setReadOnly(true);
}
if (StringUtils.isNotBlank(config.getPlaceholder())) {
props.setPlaceholder(config.getPlaceholder());
}
return props;
}
/**
* 组件属性创建完成之后,子类如果需要特殊处理,可覆写该方法
*/
protected void afterPropsCreate(FormComponentProps props, FormItemConfig config) {
}
/**
* 共用逻辑:创建约束规则、设置通用的约束规则
*/
private List<FormItemRule> createRules(FormItemConfig config) {
List<FormItemRule> rules = new ArrayList<>(4);
if (config.isRequired()) {
FormItemRule requiredRule = new FormItemRule();
requiredRule.setRequired(true);
requiredRule.setMessage("请输入" + config.getTitle());
rules.add(requiredRule);
}
return rules;
}
/**
* 约束规则创建完成之后,子类如果需要特殊处理,可覆写该方法
*/
protected void afterRulesCreate(List<FormItemRule> rules, FormItemConfig config) {
}
}

View File

@@ -0,0 +1,46 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemConfig;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemRule;
import java.util.List;
/**
* 通用文本类转换器
*
* @author F嘉阳
* @date 2022/1/26 09:54
*/
@Component
public abstract class AbstractTextConverter extends AbstractFormItemConverter {
/**
* 约束规则创建完成之后,子类如果需要特殊处理,可覆写该方法
*
* @param rules
* @param config
*/
@Override
protected void afterRulesCreate(List<FormItemRule> rules, FormItemConfig config) {
Integer minLength = config.getMinLength();
if (minLength != null && minLength > 0) {
FormItemRule minRule = new FormItemRule();
minRule.setMin(minLength);
minRule.setMessage("请至少输入 " + minLength + " 个字");
rules.add(minRule);
}
Integer maxLength = config.getMaxLength();
if (maxLength != null && maxLength > 0) {
FormItemRule maxRule = new FormItemRule();
maxRule.setMax(maxLength);
maxRule.setMessage("请最多输入 " + maxLength + " 个字");
rules.add(maxRule);
}
}
}

View File

@@ -0,0 +1,38 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.ComponentTypeEnum;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormComponentProps;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemConfig;
/**
* 下拉选择框的转换器
*
* @author F嘉阳
* @date 2022/1/26 09:51
*/
@Component
public class DropdownSelectConverter extends AbstractFormItemConverter {
/**
* 子类可处理的表单项类型
*/
@Override
public ComponentTypeEnum getType() {
return ComponentTypeEnum.DROPDOWN_SELECT;
}
/**
* 组件属性创建完成之后,子类如果需要特殊处理,可覆写该方法
*
* @param props
* @param config
*/
@Override
protected void afterPropsCreate(FormComponentProps props, FormItemConfig config) {
props.setAutoWidth(false);
if (config.isMultiple()) {
props.setMode("multiple");
}
}
}

View File

@@ -0,0 +1,37 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.ComponentTypeEnum;
import java.util.EnumMap;
import java.util.List;
/**
* 简单工厂
*
* @author F嘉阳
* @date 2022/1/26 09:56
*/
@Component
public class FormItemConverterFactory {
private static final EnumMap<ComponentTypeEnum, AbstractFormItemConverter> CONVERTER_MAP = new EnumMap<>(ComponentTypeEnum.class);
/**
* 根据表单项类型获得对应的转换器
*
* @param type 表单项类型
* @return 表单项转换器
*/
public AbstractFormItemConverter getConverter(ComponentTypeEnum type) {
return CONVERTER_MAP.get(type);
}
@Autowired
public void setConverters(List<AbstractFormItemConverter> converters) {
for (final AbstractFormItemConverter converter : converters) {
CONVERTER_MAP.put(converter.getType(), converter);
}
}
}

View File

@@ -0,0 +1,46 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.ComponentTypeEnum;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormComponentProps;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItem;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemConfig;
/**
* 模糊搜索框的转换器
*
* @author F嘉阳
* @date 2022/1/26 09:53
*/
@Component
public class FuzzySearchConverter extends AbstractFormItemConverter {
/**
* 子类可处理的表单项类型
*/
@Override
public ComponentTypeEnum getType() {
return ComponentTypeEnum.FUZZY_SEARCH;
}
/**
* 表单项创建完成之后,子类如果需要特殊处理,可覆写该方法
*
* @param item
* @param config
*/
@Override
protected void afterItemCreate(FormItem item, FormItemConfig config) {
item.setFuzzySearch(true);
}
/**
* 组件属性创建完成之后,子类如果需要特殊处理,可覆写该方法
*
* @param props
* @param config
*/
@Override
protected void afterPropsCreate(FormComponentProps props, FormItemConfig config) {
props.setAutoWidth(false);
}
}

View File

@@ -0,0 +1,61 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.ComponentTypeEnum;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemConfig;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemRule;
import java.util.List;
/**
* 此时要加入一种新的表单项 —— 数字选择器NUMBER_PICKER
* 它有着特殊的约束条件:最小值和最大值,输入到 FormItemConfig 时分别为 minNumer 和 maxNumber
* <p>
* 只需要添加对应的枚举和实现对应的 FormItemConverter并不需要修改任何逻辑代码
* 因为 Spring 启动时会自动帮我们处理好 NUMBER_PICKER 和 NumberPickerConverter 的关联关系 —— 完美符合 “开闭原则”
*
* @author F嘉阳
* @date 2022/1/26 10:13
*/
@Component
public class NumberPickerConverter extends AbstractFormItemConverter {
/**
* 子类可处理的表单项类型
*/
@Override
public ComponentTypeEnum getType() {
return ComponentTypeEnum.NUMBER_PICKER;
}
/**
* 约束规则创建完成之后,子类如果需要特殊处理,可覆写该方法
*
* @param rules
* @param config
*/
@Override
protected void afterRulesCreate(List<FormItemRule> rules, FormItemConfig config) {
Integer minNumber = config.getMinNumber();
// 处理最小值
if (minNumber != null) {
FormItemRule minNumRule = new FormItemRule();
minNumRule.setMinimum(minNumber);
minNumRule.setMessage("输入数字不能小于 " + minNumber);
rules.add(minNumRule);
}
Integer maxNumber = config.getMaxNumber();
// 处理最大值
if (maxNumber != null) {
FormItemRule maxNumRule = new FormItemRule();
maxNumRule.setMaximum(maxNumber);
maxNumRule.setMessage("输入数字不能大于 " + maxNumber);
rules.add(maxNumRule);
}
}
}

View File

@@ -0,0 +1,22 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.ComponentTypeEnum;
/**
* 多行文本框的转换器
*
* @author F嘉阳
* @date 2022/1/26 09:56
*/
@Component
public class TextAreaConvertor extends AbstractTextConverter {
/**
* 子类可处理的表单项类型
*/
@Override
public ComponentTypeEnum getType() {
return ComponentTypeEnum.TEXT_AREA;
}
}

View File

@@ -0,0 +1,22 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.ComponentTypeEnum;
/**
* 单行文本框的转换器
*
* @author F嘉阳
* @date 2022/1/26 09:55
*/
@Component
public class TextInputConverter extends AbstractTextConverter {
/**
* 子类可处理的表单项类型
*/
@Override
public ComponentTypeEnum getType() {
return ComponentTypeEnum.TEXT_INPUT;
}
}

View File

@@ -0,0 +1,20 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 09:43
*/
@Getter
@AllArgsConstructor
public enum ComponentTypeEnum {
DROPDOWN_SELECT,
FUZZY_SEARCH,
TEXT_INPUT,
TEXT_AREA,
NUMBER_PICKER;
}

View File

@@ -0,0 +1,21 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean;
import lombok.Data;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 09:40
*/
@Data
public class FormComponentProps {
private boolean readOnly;
private boolean autoWidth;
private String mode;
private String placeholder;
}

View File

@@ -0,0 +1,27 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean;
import lombok.Data;
import java.util.List;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 09:39
*/
@Data
public class FormItem {
private String title;
private String code;
private String component;
private FormComponentProps componentProps;
private boolean fuzzySearch;
private List<FormItemRule> rules;
}

View File

@@ -0,0 +1,37 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean;
import lombok.Data;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 09:39
*/
@Data
public class FormItemConfig {
private String title;
private String code;
private String component;
private boolean readOnly;
private ComponentTypeEnum type;
private boolean multiple;
private boolean required;
private Integer minLength;
private Integer maxLength;
private String placeholder;
private Integer minNumber;
private Integer maxNumber;
}

View File

@@ -0,0 +1,25 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean;
import lombok.Data;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 09:46
*/
@Data
public class FormItemRule {
private boolean required;
private String message;
private Integer min;
private Integer max;
private Integer minimum;
private Integer maximum;
}

View File

@@ -0,0 +1,104 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.before;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.*;
import java.util.ArrayList;
import java.util.List;
/**
* 原始逻辑
* <p>
* 代码违反了 开闭原则(对扩展开放,对修改关闭):如果此时需要添加一种新的表单项(包含特殊的组件属性),那么不可避免的要修改 convert 方法来进行新表单项的特殊处理。
* <p>
* 观察上面的代码,将配置转变为表单项 这个操作,满足以下流程:
* 创建表单项,并设置通用的表单项属性,然后再对不同表单项的特殊属性进行处理
* 创建组件属性,处理通用的组件属性,然后再对不同组件的特殊属性进行处理
* 创建约束规则,处理通用的约束规则,然后再对不同表单项的特性约束规则进行处理
*
* @author F嘉阳
* @date 2022/1/26 09:38
*/
public class FormItemConverter {
/**
* 将输入的配置转变为表单项
*
* @param config 前端输入的配置
* @return 表单项
*/
public FormItem convert(FormItemConfig config) {
FormItem formItem = new FormItem();
// 公共的表单项属性
formItem.setTitle(config.getTitle());
formItem.setCode(config.getCode());
formItem.setComponent(config.getComponent());
// 创建表单组件的属性
FormComponentProps props = new FormComponentProps();
formItem.setComponentProps(props);
// 公共的组件属性
if (config.isReadOnly()) {
props.setReadOnly(true);
}
ComponentTypeEnum type = config.getType();
// 下拉选择框的特殊属性处理
if (type == ComponentTypeEnum.DROPDOWN_SELECT) {
props.setAutoWidth(false);
if (config.isMultiple()) {
props.setMode("multiple");
}
}
// 模糊搜索框的特殊属性处理
if (type == ComponentTypeEnum.FUZZY_SEARCH) {
formItem.setFuzzySearch(true);
props.setAutoWidth(false);
}
// ... 其他组件的特殊处理
// 创建约束规则
List<FormItemRule> rules = new ArrayList<>(2);
formItem.setRules(rules);
// 每个表单项都可有的约束规则
if (config.isRequired()) {
FormItemRule requiredRule = new FormItemRule();
requiredRule.setRequired(true);
requiredRule.setMessage("请输入" + config.getTitle());
rules.add(requiredRule);
}
// 文本输入框才有的规则
if (type == ComponentTypeEnum.TEXT_INPUT || type == ComponentTypeEnum.TEXT_AREA) {
Integer minLength = config.getMinLength();
if (minLength != null && minLength > 0) {
FormItemRule minRule = new FormItemRule();
minRule.setMin(minLength);
minRule.setMessage("请至少输入 " + minLength + " 个字");
rules.add(minRule);
}
Integer maxLength = config.getMaxLength();
if (maxLength != null && maxLength > 0) {
FormItemRule maxRule = new FormItemRule();
maxRule.setMax(maxLength);
maxRule.setMessage("请最多输入 " + maxLength + " 个字");
rules.add(maxRule);
}
}
// ... 其他约束规则
return formItem;
}
}

View File

@@ -0,0 +1,34 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.after;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Building;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Factory;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Node;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.School;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 10:31
*/
public class DrawVisitor implements Visitor {
@Override
public void visit(Node node) {
System.out.println("draw node");
}
@Override
public void visit(Factory factory) {
System.out.println("draw factory");
}
@Override
public void visit(Building building) {
System.out.println("draw building");
}
@Override
public void visit(School school) {
System.out.println("draw school");
}
}

View File

@@ -0,0 +1,23 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.after;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Building;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Factory;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Node;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.School;
/**
* Visitor Pattern
*
* @author F嘉阳
* @date 2022/1/26 10:31
*/
public interface Visitor {
void visit(Node node);
void visit(Factory factory);
void visit(Building building);
void visit(School school);
}

View File

@@ -0,0 +1,30 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.after.Visitor;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 10:18
*/
public class Building implements Node {
@Override
public String getName() {
return null;
}
@Override
public String getDescription() {
return null;
}
/**
* 调用方知道visit的参数就是Building类型的并且知道Visitor::visit(Building)方法确实存在,
* 因此会直接调用Visitor::visit(Building)方法
*/
@Override
public void accept(Visitor v) {
v.visit(this);
}
}

View File

@@ -0,0 +1,30 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.after.Visitor;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 10:18
*/
public class Factory implements Node {
@Override
public String getName() {
return null;
}
@Override
public String getDescription() {
return null;
}
/**
* 调用方知道visit的参数就是Factory类型的并且知道Visitor::visit(Factory)方法确实存在,
* 因此会直接调用Visitor::visit(Factory)方法
*/
@Override
public void accept(Visitor v) {
v.visit(this);
}
}

View File

@@ -0,0 +1,18 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.after.Visitor;
/**
* 地图节点
*
* @author F嘉阳
* @date 2022/1/26 10:17
*/
public interface Node {
String getName();
String getDescription();
void accept(Visitor v);
}

View File

@@ -0,0 +1,30 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.after.Visitor;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/1/26 10:18
*/
public class School implements Node {
@Override
public String getName() {
return null;
}
@Override
public String getDescription() {
return null;
}
/**
* 调用方知道visit的参数就是School类型的并且知道Visitor::visit(School)方法确实存在,
* 因此会直接调用Visitor::visit(School)方法
*/
@Override
public void accept(Visitor v) {
v.visit(this);
}
}

View File

@@ -0,0 +1,33 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring.before;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Building;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Factory;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Node;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.School;
/**
* 新需求需要加入绘制Node功能
* Node接口作为衔接组件的桥梁应该尽可能保持稳定不应频繁更改。
* 定义一个新的类DrawService把所有的draw逻辑都写在这里面
*
* @author F嘉阳
* @date 2022/1/26 10:18
*/
public class DrawService {
public void draw(Building building) {
System.out.println("draw building");
}
public void draw(Factory factory) {
System.out.println("draw factory");
}
public void draw(School school) {
System.out.println("draw school");
}
public void draw(Node node) {
System.out.println("draw node");
}
}

View File

@@ -1,8 +1,5 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod;
import sun.misc.Launcher;
import java.util.Collection;
/**
* 定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中

View File

@@ -0,0 +1,27 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.after;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean.FormDataHandler;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean.FormItemConverter;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean.FormItemTypeEnum;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 14:26
*/
@Configuration
public class FactoryConfig {
@Bean
public StrategyFactory<String, FormDataHandler> formDataHandlerFactory() {
return new StrategyFactory<>(FormDataHandler.class);
}
@Bean
public StrategyFactory<FormItemTypeEnum, FormItemConverter> formItemConverterFactory() {
return new StrategyFactory<>(FormItemConverter.class);
}
}

View File

@@ -0,0 +1,15 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.after;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:42
*/
public interface Strategy<T> {
/**
* 获得策略的标识
*/
T getId();
}

View File

@@ -0,0 +1,64 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.after;
import com.google.common.collect.Maps;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Collection;
import java.util.Map;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:42
*/
public class StrategyFactory<T, S extends Strategy<T>> implements InitializingBean, ApplicationContextAware {
private Map<T, S> strategyMap;
private ApplicationContext appContext;
private final Class<S> strategyType;
/**
* 创建一个策略工厂
*
* @param strategyType 策略的类型
*/
public StrategyFactory(Class<S> strategyType) {
this.strategyType = strategyType;
}
/**
* 根据策略 id 获得对应的策略的 Bean
*
* @param id 策略 id
* @return 策略的 Bean
*/
public S getStrategy(T id) {
return strategyMap.get(id);
}
@Override
public void afterPropertiesSet() throws Exception {
// 获取 Spring 容器中,所有 S 类型的 Bean
Collection<S> strategies = appContext.getBeansOfType(strategyType).values();
strategyMap = Maps.newHashMapWithExpectedSize(strategies.size());
// 将 所有 S 类型的 Bean 放入到 strategyMap 中
for (final S strategy : strategies) {
T id = strategy.getId();
strategyMap.put(id, strategy);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
appContext = applicationContext;
}
}

View File

@@ -0,0 +1,10 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 14:16
*/
public class CommonResponse<T> {
}

View File

@@ -0,0 +1,25 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean;
import top.fjy8018.designpattern.pattern.behavior.strategy.spring.FormSubmitRequest;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.after.Strategy;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:37
*/
public interface FormDataHandler extends Strategy<String> {
/**
* 获得策略的标识
*/
@Override
default String getId() {
return getFormCode();
}
String getFormCode();
CommonResponse<Object> submit(FormSubmitRequest request);
}

View File

@@ -0,0 +1,26 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItem;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemConfig;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.after.Strategy;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:40
*/
public interface FormItemConverter extends Strategy<FormItemTypeEnum> {
/**
* 获得策略的标识
*/
@Override
default FormItemTypeEnum getId() {
return getType();
}
FormItemTypeEnum getType();
FormItem convert(FormItemConfig config);
}

View File

@@ -0,0 +1,10 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:39
*/
public enum FormItemTypeEnum {
}

View File

@@ -0,0 +1,39 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean.FormDataHandler;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:36
*/
@Component
public class FormDataHandlerFactory {
private static final
Map<String, FormDataHandler> FORM_DATA_HANDLER_MAP = new HashMap<>(16);
/**
* 根据表单标识,获取对应的 Handler
*
* @param formCode 表单标识
* @return 表单对应的 Handler
*/
public FormDataHandler getHandler(String formCode) {
return FORM_DATA_HANDLER_MAP.get(formCode);
}
@Autowired
public void setFormDataHandlers(List<FormDataHandler> handlers) {
for (FormDataHandler handler : handlers) {
FORM_DATA_HANDLER_MAP.put(handler.getFormCode(), handler);
}
}
}

View File

@@ -0,0 +1,39 @@
package top.fjy8018.designpattern.pattern.creational.factorymethod.spring.before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean.FormItemConverter;
import top.fjy8018.designpattern.pattern.creational.factorymethod.spring.bean.FormItemTypeEnum;
import java.util.EnumMap;
import java.util.List;
/**
* TODO:
*
* @author F嘉阳
* @date 2022/4/8 13:38
*/
@Component
public class FormItemConverterFactory {
private static final
EnumMap<FormItemTypeEnum, FormItemConverter> CONVERTER_MAP = new EnumMap<>(FormItemTypeEnum.class);
/**
* 根据表单项类型获得对应的转换器
*
* @param type 表单项类型
* @return 表单项转换器
*/
public FormItemConverter getConverter(FormItemTypeEnum type) {
return CONVERTER_MAP.get(type);
}
@Autowired
public void setConverters(List<FormItemConverter> converters) {
for (final FormItemConverter converter : converters) {
CONVERTER_MAP.put(converter.getType(), converter);
}
}
}

View File

@@ -0,0 +1,20 @@
package top.fjy8018.designpattern.pattern.behavior.strategy.spring;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Component
class FormSubmitHandlerTest {
@Autowired
private FormSubmitHandlerFactory factory;
@Test
void getSubmitType() {
FormSubmitHandler<Serializable> handler = factory.getHandler("preview");
handler.handleSubmit(new FormSubmitRequest());
}
}

View File

@@ -0,0 +1,38 @@
package top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.after;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItem;
import top.fjy8018.designpattern.pattern.behavior.templatemethod.spring.bean.FormItemConfig;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Component
class FormItemConverterFactoryTest {
@Autowired
private FormItemConverterFactory factory;
public List<FormItem> convertFormItems(JSONArray inputConfigs) {
return IntStream.range(0, inputConfigs.size())
.mapToObj(inputConfigs::getJSONObject)
.map(this::convertFormItem)
.collect(Collectors.toList());
}
private FormItem convertFormItem(JSONObject inputConfig) {
FormItemConfig itemConfig = inputConfig.toJavaObject(FormItemConfig.class);
AbstractFormItemConverter converter = factory.getConverter(itemConfig.getType());
if (converter == null) {
throw new IllegalArgumentException("不存在转换器:" + itemConfig.getType());
}
return converter.convert(itemConfig);
}
}

View File

@@ -0,0 +1,76 @@
package top.fjy8018.designpattern.pattern.behavior.visitor.spring;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.after.DrawVisitor;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.after.Visitor;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Building;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Factory;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.Node;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.bean.School;
import top.fjy8018.designpattern.pattern.behavior.visitor.spring.before.DrawService;
/**
* DrawService中只有4个draw方法参数类型分别是Factory, Building, School和Node如果调用方传了一个City进来怎么办
* 毕竟调用方可以自己实现一个City类传进来。这种情况下程序该调用什么方法呢
* 我们没有draw(City)方法为了防止这种情况发生程序在编译阶段就直接选择使用DrawService::draw(Node)方法。
* 无论调用方传了什么实现进来我们都统一使用DrawService::draw(Node)方法以确保程序安全运行。
* 在编译时而不是运行时决定调用哪个方法这就叫做Static/Early Binding。
* 这也就解释了我们为什么输出了 draw node 。
*/
@Slf4j
class DrawServiceTest {
/**
* 输出draw node
*/
@Test
public void draw() {
draw(new Factory());
}
/**
* 输出draw factory
*/
@Test
public void draw17() {
draw17(new Factory());
}
@Test
public void draw2() {
Visitor drawVisitor = new DrawVisitor();
Factory factory = new Factory();
factory.accept(drawVisitor);
}
private void draw(Node node) {
DrawService drawService = new DrawService();
if (node instanceof Building) {
Building building = (Building) node;
drawService.draw(building);
} else if (node instanceof Factory) {
Factory factory = (Factory) node;
drawService.draw(factory);
} else if (node instanceof School) {
School school = (School) node;
drawService.draw(school);
} else {
drawService.draw(node);
}
}
private void draw17(Node node) {
DrawService drawService = new DrawService();
if (node instanceof Building building) {
drawService.draw(building);
} else if (node instanceof Factory factory) {
drawService.draw(factory);
} else if (node instanceof School school) {
drawService.draw(school);
} else {
drawService.draw(node);
}
}
}