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

相关文章

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.