JavaWeb可扩展性之模板方法的运用

2024-01-28 22:32

本文主要是介绍JavaWeb可扩展性之模板方法的运用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

服务编织时用模板方法模式是一种非常实用技巧,通过模板方法定义出服务基本操作、日志、异常处理等,也方便做限流、报警、流量统计等。这里的可扩展性体现在,当需要实现新添加的服务时,只需要套用模板,实现差异点就可以了。当然模板对可扩展点的定义和粒度都会影响具体的效果。

以API服务的实现为例,实现一个简单模板,有基本的日志、异常处理,代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;// API服务实现模板
public abstract class ApiServiceTemplate<R, P> {protected Logger logger = LoggerFactory.getLogger(getClass());// 业务逻辑入口protected abstract R process() throws BizException;// 接口参数protected abstract P getParam();// 模板执行入口public Response<R> execute() {long startTime = System.currentTimeMillis();logger.info("Enter service, params:{}", getParam().toString());try {R result = process();return new Response<R>(SystemCode.BIZ_SUCCESS, result);} catch (BizException e) {logger.error("Api sevice execute Bizexception", e);return new Response<R>(SystemCode.BIZ_FAILED, null);} catch (Throwable e) {logger.error("Api sevice execute error", e);return new Response<R>(SystemCode.BIZ_FAILED, null);} finally {long totalTime = System.currentTimeMillis() - startTime;if (totalTime > 200) {logger.warn("This method used too long time, please check and optimize it:{}ms", totalTime);}}}
}// 其中Response、SystemCode和BizException的代码我删掉了,只突出重点

针对一些具体的业务流程,模板方法的作用更明显,业务的具体实现是应用的核心代码,这部分可复用的价值很高,但相应地,问题也很多。主要有两点:

问题一:合作开发成本高。别人实现的带有强烈主观意愿的设计思路,往往都不那么好理解。且不好的设计尤其是各种“专属设定”往往会隐藏着坑!我曾经接手一个应用,服务实现对流程和操作抽象度很高,甚至BO都是继承了N层的接口,结果来来回回看好几遍都不清楚层次结构,每层的定位是什么。

问题二:盲目复用和多层复用会导致代码后期维护非常复杂。看下面这个吃牛肉的例子:

/*** Created by song on 2019/1/5.*/// 错误示范
public interface Eat {// 吃牛肉public void eatBeaf();}// 一般吃法,作为基本实现
abstract class NormalEat implements Eat{public void eatBeaf() {// 第一步,做熟cookBeaf();// 第二步,吃牛肉eatTheBeaf();}protected void cookBeaf() {System.out.println("roast beaf");}protected void eatTheBeaf() {System.out.println("eat with hand");}}// 西式吃法
class WestEat extends NormalEat{@Overrideprotected void eatTheBeaf() {System.out.println("eat with knife & folk");}}// 东方吃法
class EestEat extends NormalEat{@Overrideprotected void cookBeaf() {System.out.println("hot pot beaf");}@Overrideprotected void eatTheBeaf() {System.out.println("eat with chopsticks");}}// 中式吃法
class ChineseEat extends EestEat{@Overrideprotected void cookBeaf() {super.cookBeaf();// 中式吃法加很多辣椒System.out.println(" add plenty of chilli");}}// 日式吃法
class JapaneseEat extends EastEat{@Overrideprotected void eatTheBeaf() {talkBeforeEat();super.eatTheBeaf();}protected void talkBeforeEat() {// 日本人吃之前先说:"我开动了"System.out.print("いただきます");}}// 日本北海道吃法
class JapaneseBhdEat extends JapaneseEat{@Overrideprotected void talkBeforeEat() {// 北海道人吃之前不说"我开动了"(我胡诌的)}}// 日本北海道札幌市吃法
class JapaneseBhdZhEat extends JapaneseBhdEat{@Overrideprotected void cookBeaf() {super.cookBeaf();// 煮牛肉火锅加咸鱼System.out.println("add salt fish");}}

例子中讲牛肉的吃法,最基础实现NormalEat中定义了基本实现分两步:做牛肉cookBeaf和吃牛肉eatTheBeaf,到“日本北海道”吃法中,由于复用了父类JapaneseEat的eatTheBeaf和祖父类EastEat的cookBeaf,想知道北海道是怎么吃的,就需要一层层查上去。这是个非常简单的例子,实际应用中的业务场景会复杂的多,层数如果很多,而中间层又有很多差异化的“共性实现”,那么将是一个灾难,这样一层层集成下去,结果就是北海道札幌市鬼子村的松尾桑在做牛肉和吃牛肉之间做了什么,没有人能说清楚,也不知道他有没有说“我开动了”!

那么怎么解决这个问题呢?

1)不能分层太多,最多不能超过3层,比如北海道札幌市鬼子村的松尾桑吃牛肉的方法,就用一个和北海道平级的实现,虽然这一层的实现变多了,但很清晰,最多找两层就清楚所有实现了。

2)一些共用实现方法从模板里拆出来,作为独立完整的小服务,调用出只关心出入口。这样既使代码结构清晰,又使得服务的实现更加稳定且便于维护扩展。

对上例做修改如下:

// 再来一顿,改进版
public interface EatAgain {// 吃牛肉public void eatBeaf();
}// 服务模板抽象
abstract class AbsEatAgain implements EatAgain{// 做牛肉方法public abstract CookBeafHandler getCookBeafHandler();// 吃牛肉方法public abstract EatTheBeafHandler getEatTheBeafHandler();public void eatBeaf() {// 第一步,做熟getCookBeafHandler().doIt();// 第二步,吃牛肉getEatTheBeafHandler().doIt();}}// 做牛肉方法
interface CookBeafHandler {// 做牛肉public void doIt();}// 吃牛肉方法
interface EatTheBeafHandler {// 吃牛肉方法public void doIt();}// 一般吃法,作为基本实现
class NormalEat extends AbsEatAgain{public CookBeafHandler getCookBeafHandler() {return new BaseCookBeafHandler();}public EatTheBeafHandler getEatTheBeafHandler() {return new BaseEatTheBeafHandler();}
}// 西式吃法
class WestEat extends NormalEat{public EatTheBeafHandler getEatTheBeaf() {return new KnifeFolkEatTheBeafHandler();}}// 东方吃法
class EestEat extends NormalEat{public CookBeafHandler getCookBeafHandler() {return new HotPotCookBeafHandler();}public EatTheBeafHandler getEatTheBeafHandler() {return new EatWithChopsticksHandler();}}// 中式吃法
class ChineseEat extends NormalEat {public CookBeafHandler getCookBeafHandler() {return new ChilliCookBeafHandler();}
}// 日式吃法
class JapaneseEat extends NormalEat {public EatTheBeafHandler getEatTheBeafHandler() {return new TalkAndEatTheBeafHandler();}}// 日本北海道吃法
class JapaneseBhdEat extends NormalEat {// 可以直接用NormalEat,因为跟基本吃法是一样的
}// 日本北海道札幌市吃法
class JapaneseBhdZhEat extends NormalEat {public CookBeafHandler getCookBeafHandler() {return new SaltFishCookBeafHandler();}
}

所有的Handler:

// 基本做牛肉方法
class BaseCookBeafHandler implements CookBeafHandler{public void doIt() {System.out.println("roast beaf");}
}// 基本吃牛肉方法
class BaseEatTheBeafHandler implements EatTheBeafHandler{// 吃牛肉public void doIt() {System.out.println("eat with hand");}}// 吃牛肉方法:刀叉吃法
class KnifeFolkEatTheBeafHandler extends BaseEatTheBeafHandler{// 吃牛肉方法public void doIt() {System.out.println("eat with knife & folk");}}// 做法:火锅牛肉
class HotPotCookBeafHandler extends BaseCookBeafHandler {public void doId() {System.out.println("hot pot beaf");}
}// 吃牛肉方法:用筷子吃
class EatWithChopsticksHandler extends BaseEatTheBeafHandler {public void doId() {System.out.println("eat with chopsticks");}
}// 做法:加辣椒的做法
class ChilliCookBeafHandler extends BaseCookBeafHandler {public void doId() {super.doIt();System.out.println("add plenty of chilli");}}// 吃牛肉方法:说完再吃
class TalkAndEatTheBeafHandler extends BaseEatTheBeafHandler {public void doId() {System.out.print("いただきます");super.doIt();}
}// 做法:加咸鱼做法
class SaltFishCookBeafHandler extends BaseCookBeafHandler {public void doId() {super.doIt();System.out.println("add salt fish");}}

上述代码中,有两项重要调整:一是将模板中的两个重要节点“做牛肉”和“吃牛肉”方法拆出来,建立专门接口和实现,这样做的好处是这些功能点完全独立,可复用性更好,并且作为独立完整的小模块功能可以更强大和稳定;二是不再出现超多层继承,服务的每个实现都很清晰做了哪些事。相对来说,虽然比之前的实现多了很多Handler,并且没有做到“只要能复用就复用”,但层次会清晰很多,扩展和管理更方便。

现在如果北海道札幌市鬼子村的松尾桑想要吃牛肉,那么只需要添加一个SongWeiEat继承AbsEatAgain,并实现继承BaseCookBeafHandler和BaseEatTheBeafHandler的Handler就可以了。

这篇关于JavaWeb可扩展性之模板方法的运用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/654982

相关文章

python获取网页表格的多种方法汇总

《python获取网页表格的多种方法汇总》我们在网页上看到很多的表格,如果要获取里面的数据或者转化成其他格式,就需要将表格获取下来并进行整理,在Python中,获取网页表格的方法有多种,下面就跟随小编... 目录1. 使用Pandas的read_html2. 使用BeautifulSoup和pandas3.

SpringBoot UserAgentUtils获取用户浏览器的用法

《SpringBootUserAgentUtils获取用户浏览器的用法》UserAgentUtils是于处理用户代理(User-Agent)字符串的工具类,一般用于解析和处理浏览器、操作系统以及设备... 目录介绍效果图依赖封装客户端工具封装IP工具实体类获取设备信息入库介绍UserAgentUtils

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

Spring Boot中JSON数值溢出问题从报错到优雅解决办法

《SpringBoot中JSON数值溢出问题从报错到优雅解决办法》:本文主要介绍SpringBoot中JSON数值溢出问题从报错到优雅的解决办法,通过修改字段类型为Long、添加全局异常处理和... 目录一、问题背景:为什么我的接口突然报错了?二、为什么会发生这个错误?1. Java 数据类型的“容量”限制

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

SpringBoot项目中报错The field screenShot exceeds its maximum permitted size of 1048576 bytes.的问题及解决

《SpringBoot项目中报错ThefieldscreenShotexceedsitsmaximumpermittedsizeof1048576bytes.的问题及解决》这篇文章... 目录项目场景问题描述原因分析解决方案总结项目场景javascript提示:项目相关背景:项目场景:基于Spring