模板模式的引进
【第一版】中国人民银行发送短信例子,抽象发送的内容,发送短信的思路一致,内容不一样。
- 抽象短信发送模板
public abstract class AbstractSms {
public void sendSms() {
// 模拟参数传入
Map context = new HashMap<>();
context.put("name", "张三");
context.put("money", 100);
System.out.println("会话设置成功...");
// 设置发送短信内容
String fillSms = fillSms(context);
// 发送短信,这里直接输出
System.out.println("==》发送短信:" + fillSms);
}
/**
* 填充短信内容
*/
protected abstract String fillSms(Map context);
}
- 存钱模板
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 存钱
*/
public class AddSms extends AbstractSms{
@Override
protected String fillSms(Map context) {
String message = "{NAME}在{TIME},存钱{MONEY}。【中国人民银行】";
Map<String, Object> map = new HashMap<>();
map.put("NAME", context.get("name"));
map.put("TIME", "2019.4.13");
map.put("MONEY", context.get("money"));
String tagerStr = PlaceHolderReplaceUtils.replaceWithMap(message, map);
return tagerStr;
}
}
- 取钱模板
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 取钱
*/
public class SubSms extends AbstractSms{
@Override
protected String fillSms(Map context) {
String message = "{NAME}在{TIME},取钱{MONEY}。【中国人民银行】";
Map<String, Object> map = new HashMap<>();
map.put("NAME", context.get("name"));
map.put("TIME", "2019.4.13");
map.put("MONEY", context.get("money"));
String tagerStr = PlaceHolderReplaceUtils.replaceWithMap(message, map);
return tagerStr;
}
}
- 编写替换工具类,占位符替换
/**
* Desc: 占位符替换, 占位符表示为:{@code {placeholder}};
* <p>
* 示例:替换如下{xxx}占位符中的内容
*
* <pre>
* "名字:{name},年龄:{age},学校:{school}"
* </pre>
*/
public class PlaceHolderReplaceUtils {
private static final Pattern pattern = Pattern.compile("\\{(.*?)\\}");
private static Matcher matcher;
/**
* 替换字符串占位符, 字符串中使用{key}表示占位符
*
* @param sourceString
* 需要匹配的字符串,示例:"名字:{name},年龄:{age},学校:{school}";
* @param param
* 参数集,Map类型
* @return
*/
public static String replaceWithMap(String sourceString, Map<String, Object> param) {
if (isNullOrEmptyString(sourceString) || isEmptyMap(param)) {
return sourceString;
}
String targetString = sourceString;
matcher = pattern.matcher(sourceString);
while (matcher.find()) {
try {
String key = matcher.group();
String keyclone = key.substring(1, key.length() - 1).trim();
Object value = param.get(keyclone);
if (value != null) {
targetString = targetString.replace(key, value.toString());
}
} catch (Exception e) {
throw new RuntimeException("String formatter failed", e);
}
}
return targetString;
}
private static boolean isEmptyMap(Map<String, Object> param) {
if (param == null || param.isEmpty()) {
return true;
}
return false;
}
private static boolean isNullOrEmptyString(String sourceString) {
if (sourceString == null || "".equals(sourceString)) {
return true;
}
return false;
}
}
- 测试
public class TestSms {
public static void main(String[] args) {
AbstractSms sms = new AddSms();
sms.sendSms();
}
}
测试成功
当前问题
- 发送短信模板,替换内容及用户代码耦合性较高,需要拆分
- 是否需要短信提醒设置有缺陷,可能有的用户不需要提醒
细化模板模式,对上一问题改进
【第二版】中国人民银行发送短信例子,细化抽象短信
- 抽象短信发送模板
public abstract class AbstractSms {
public void sendSms() {
// 模拟参数传入
Map context = new HashMap<>();
context.put("name", "张三");
context.put("money", 100);
System.out.println("会话设置成功...");
// 判断是否发送短信, 使用卫语句,校验层次
if (!isSendSms(context)) {
System.out.println("当前用户取消发送短信");
return;
}
// 获取短信模板
String smsTemplate = smsTemplate(context);
// 获取参数替换
Map smsParam = smsParam(context);
// 最终短信
String tagerStr = PlaceHolderReplaceUtils.replaceWithMap(smsTemplate, smsParam);
// 发送短信,这里直接输出
System.out.println("==》发送短信:" + tagerStr);
}
/**
* 短信模板
*/
protected abstract String smsTemplate(Map context);
/**
* 参数替换
*/
protected abstract Map smsParam(Map context);
/**
* 是否发送短信
*/
protected abstract boolean isSendSms(Map context);
}
- 存钱模板
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 存钱
*/
public class AddSms extends AbstractSms{
@Override
protected String smsTemplate(Map context) {
String message = "{NAME}在{TIME},存钱{MONEY}。【中国人民银行】";
return message;
}
@Override
protected Map smsParam(Map context) {
Map<String, Object> map = new HashMap<>();
map.put("NAME", context.get("name"));
map.put("TIME", "2019.4.13");
map.put("MONEY", context.get("money"));
return map;
}
@Override
protected boolean isSendSms(Map context) {
return true;
}
}
- 取钱模板
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 取钱
*/
public class SubSms extends AbstractSms{
@Override
protected String smsTemplate(Map context) {
String message = "{NAME}在{TIME},取钱{MONEY}。【中国人民银行】";
return message;
}
@Override
protected Map smsParam(Map context) {
Map<String, Object> map = new HashMap<>();
map.put("NAME", context.get("name"));
map.put("TIME", "2019.4.13");
map.put("MONEY", context.get("money"));
return map;
}
@Override
protected boolean isSendSms(Map context) {
// 张三取消发送短信
if ("张三".equals(context.get("name"))) {
return false;
}
return true;
}
}
- 测试
public class TestSms {
public static void main(String[] args) {
AbstractSms sms = new AddSms();
sms.sendSms();
sms = new SubSms();
sms.sendSms();
}
}
测试成功
基本架构已经成功,也能满足我们后期扩展的需求,但是还是会有以下问题
- sendSms方法中,有与发送短信无关的功能
- 当前模板抽象方法众多,出现类似家族
- 后期扩展新的抽象方法AbstractSms的子类,现有实现,如AddSms的实现,代码如何复用,
这里可能稍微难理解,也就是类AddSms的三个实现,我想复用在其他AbstractSms的子类,但是我不通过代码拷贝
- 如何要继续抽象新的想法,现有的AbstractSms子类如何对新增抽象方法不产生影响
桥连模式的引入
- 抽象原短信内容方法为统一接口
public interface SmsDefine {
/**
* 短信模板
*/
String smsTemplate(Map context);
/**
* 参数替换
*/
Map smsParam(Map context);
/**
* 是否发送短信
*/
boolean isSendSms(Map context);
}
- 修改原短信内容继承,存钱
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 存钱
*/
public class AddSms implements SmsDefine{
@Override
public String smsTemplate(Map context) {
String message = "{NAME}在{TIME},存钱{MONEY}。【中国人民银行】";
return message;
}
@Override
public Map smsParam(Map context) {
Map<String, Object> map = new HashMap<>();
map.put("NAME", context.get("name"));
map.put("TIME", "2019.4.13");
map.put("MONEY", context.get("money"));
return map;
}
@Override
public boolean isSendSms(Map context) {
return true;
}
}
- 修改原短信内容继承,取钱
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 取钱
*/
public class SubSms implements SmsDefine{
@Override
public String smsTemplate(Map context) {
String message = "{NAME}在{TIME},取钱{MONEY}。【中国人民银行】";
return message;
}
@Override
public Map smsParam(Map context) {
Map<String, Object> map = new HashMap<>();
map.put("NAME", context.get("name"));
map.put("TIME", "2019.4.13");
map.put("MONEY", context.get("money"));
return map;
}
@Override
public boolean isSendSms(Map context) {
// 张三取消发送短信
if ("张三".equals(context.get("name"))) {
return false;
}
return true;
}
}
- 更改原短信发送模板
/**
*
* Created by it
* Created in 2019年4月13日
* Description: 真正意义上模板,外加SmsDefine桥连
*/
public abstract class AbstractSms {
// SmsDefine桥连
protected SmsDefine smsDefine;
// 全局上线文
protected Map context = new HashMap<>();
public AbstractSms(SmsDefine smsDefine) {
this.smsDefine = smsDefine;
}
/**
* 类似Thread提供统一的入口方法
*/
public void start() {
initParam();
sendSms();
destory();
}
// 初始化
protected abstract void initParam();
// 发送短信
protected abstract void sendSms();
// 结束阶段,可记录日志等
protected abstract void destory();
}
- 默认实现一套短信模板
public class DefaultSms extends AbstractSms {
public DefaultSms(SmsDefine smsDefine) {
super(smsDefine);
}
@Override
protected void initParam() {
// 模拟参数传入
context.put("name", "张三");
context.put("money", 100);
System.out.println("会话设置成功...");
}
@Override
protected void sendSms() {
// 判断是否发送短信, 使用卫语句,校验层次
if (!smsDefine.isSendSms(context)) {
System.out.println("当前用户取消发送短信");
return;
}
// 获取短信模板
String smsTemplate = smsDefine.smsTemplate(context);
// 获取参数替换
Map smsParam = smsDefine.smsParam(context);
// 最终短信
String tagerStr = PlaceHolderReplaceUtils.replaceWithMap(smsTemplate, smsParam);
// 发送短信,这里直接输出
System.out.println("==》发送短信:" + tagerStr);
}
@Override
protected void destory() {
System.out.println("原数据归档");
System.out.println("日志记录");
}
}
- 测试
public class TestSms {
public static void main(String[] args) {
// 短信内容
SmsDefine smsDefine = new AddSms();
// 短信流程
AbstractSms sms = new DefaultSms(smsDefine);
// 开发发送
sms.start();
}
}
桥连模式的扩展验证
场景:短信模板数据查询,需要先查询redis缓存中是否有配置,没有再取其他地方配置
- 扩展短信功能
public interface CacheSmsDefine extends SmsDefine {
String smsTemplateFromCache(Map context);
}
- 扩展流程模板
public class RedisCacheSms extends AbstractSms {
public RedisCacheSms(SmsDefine smsDefine) {
super(smsDefine);
}
@Override
protected void initParam() {
// 模拟参数传入
context.put("name", "张三");
context.put("money", 100);
System.out.println("会话设置成功...");
}
@Override
protected void sendSms() {
// 判断是否发送短信, 使用卫语句,校验层次
if (!smsDefine.isSendSms(context)) {
System.out.println("当前用户取消发送短信");
return;
}
// 获取短信模板
String smsTemplate = "";
if (smsDefine instanceof CacheSmsDefine) {
CacheSmsDefine cacheSmsDefine = (CacheSmsDefine) smsDefine;
smsTemplate = cacheSmsDefine.smsTemplateFromCache(context);
}
if (smsTemplate == null || "".equals(smsTemplate)) {
smsDefine.smsTemplate(context);
System.out.println("将短信模板数据进行缓存设置");
}
// 获取参数替换
Map smsParam = smsDefine.smsParam(context);
// 最终短信
String tagerStr = PlaceHolderReplaceUtils.replaceWithMap(smsTemplate, smsParam);
// 发送短信,这里直接输出
System.out.println("==》发送短信:" + tagerStr);
}
@Override
protected void destory() {
System.out.println("原数据归档");
System.out.println("日志记录");
}
}
以上改造有两点,将业务流程与业务的具体内容完全解耦,具有以下优点
- 可动态扩展短信内容
- 可动态扩展业务流程模板
- 不修改现有功能