作者 张强
张强,16年加入京东,目前就职于京东商城京麦平台组,从事京东对外开放平台和服务于第三方入驻商家的相关工作,开源爱好者,对常用开源框架如Spring、Mybatis、Dubbo等有源码级别的了解,热衷于研究各种技术,致力于成为一名有腔调的工程师
最近在开发过程中需要修改这样一段代码:
InvoiceSubmitter invoiceSubmitter;
if (是图书业务) {
if (是增值税专用发票) {
invoiceSubmitter = rjVatInvoiceSubmitter;
} else {
invoiceSubmitter = rjGeneralInvoiceSubmitter;
}
} else {
invoiceSubmitter = commonInvoiceSubmitter;
}
invoiceSubmitter.submitRequisition(order);
类图:
imageOrderFinishSupport直接关联InvoiceSubmitter接口,AbstractInvoiceSubmitter实现InvoiceSubmitter接口submitRequisition方法封装通用逻辑,并定义另外一个重载的submitRequisition方法供子类实现具体业务逻辑。
这是一段发票提交的逻辑,在我们系统的订单完成之后,需要根据用户提交的发票内容调用不同的接口将发票信息提交到发票系统,发票系统来完成为用户开具发票的工作。
这段代码目前看起来还算清晰,就是根据业务和发票的类型判断调用哪个接口,但是这次需要修改这段代码,加上分支判断逻辑区分另外一种业务,未来还会承接更多的需求,这样这段代码的条件分支判断会越来越庞大,可读性会越来越差,也越来越难维护。
重构开始,这段逻辑整体看起来就是一条链,每个submitter处理满足自己条件的请求,所以责任链模式可以很好的解决这个问题,重构之后结构如下:
image抽象InvoiceSubmitHandler,OrderFinishSupport改为关联InvoiceSubmitHandler,在InvoiceSubmitHandler的初始化方法init中调用各个submitter的setNextHandler方法构建好整个责任链,每个具体的submitter通过match方法返回自己是否能够处理本次请求的波尔值,AbstractInvoiceSubmitter会根据这个返回值判断用当前的submitter处理还是调用下一个nextHandler来处理,关键代码如下:
InvoiceSubmitHandler类:
class InvoiceSubmitHandler {
private InvoiceSubmitter commonInvoiceSubmitter;
private InvoiceSubmitter rjGeneralInvoiceSubmitter;
private InvoiceSubmitter rjVatInvoiceSubmitter;
/**
* 初始化链条,设置handler的下一个handler
*/
public void init() {
commonInvoiceSubmitter.setNextHandler(rjGeneralInvoiceSubmitter);
rjGeneralInvoiceSubmitter.setNextHandler(rjVatInvoiceSubmitter);
}
/**
* 提交发票申请单
*/
public void submitRequisition() {
commonInvoiceSubmitter.submitRequisition();
}
}
AbstractInvoiceSubmitter类:
abstract class AbstractInvoiceSubmitter implements InvoiceSubmitter {
/**
* 下一个handler
*/
private InvoiceSubmitter nextHandler;
/**
* 提交申请单
*/
public void submitRequisition() {
if (match()) {
submitRequisition();
return;
}
if (nextHandler == null) {
return; //如果下一个handler为空,证明当前handler已经是最后一个handler,直接返回,证明本次请求不需要处理
}
nextHandler.submitRequisition();
}
/**
* 判断当前handler是否能够处理当前请求
*/
protected abstract boolean match();
/**
* 设置下一个handler
*/
public void setNextHandler(InvoiceSubmitter handler) {
this.nextHandler = handler;
}
/**
* 子类具体实现提交发票申请单逻辑
*/
protected abstract void submitRequisition();
}
RJGeneralInvoiceSubmitter类:
class RJGeneralInvoiceSubmitter extends AbstractInvoiceSubmitter {
/**
* 判断当前handler是否能够处理当前请求
*/
protected boolean match() {
return 是图书业务 && 发票类型为增值税普通发票;
}
/**
* 提交申请单
*/
public void submitRequisition() {
...
}
}
class RJGeneralInvoiceSubmitter extends AbstractInvoiceSubmitter {
/**
* 判断当前handler是否能够处理当前请求
*/
protected boolean match() {
return 是图书业务 && 发票类型为增值税普通发票;
}
/**
* 提交申请单
*/
public void submitRequisition() {
...
}
}
具体submitter只列出一个,其他类似。重构之后,具体的匹配逻辑分散到各个子类中实现,类的职责更加明确和集中,代码可读性、扩展性更好。在使用责任链模式的时候有一个需要注意的点,我们需要保证整个链一定要有一个节点能够处理当前请求,不然在请求传递到最后一个节点还处理不了时,会报空指针异常,因为最后一个节点的nextHandler没有设置,我们这里的处理是判断如果nextHandler为空,证明当前节点已经是最后一个,直接返回,证明本次请求不需要提交发票申请。也可以定义一个可以匹配所有请求的最终节点,当前面的所有节点都无法处理的时候由这个节点做一个默认处理。
最后,写一点自己的代码结构的看法,作为一个有腔调的工程师,我们要把我们写过的代码当做一件艺术品,不要放过上面任何的一点瑕疵,有“代码洁癖”完全不是一件坏事,在仔细雕琢的过程中,我们会有很多收获,也会让自己更好的成长。
LinkedKeeper微信公众号
网友评论