前言
有段时间没写文章了,最近沉迷 Rust,无法自拔,锈儿有毒;这真是门非常有趣的语言,很多地方的设计,真的是满足了我所有的向往。
当然,这也不是一门简单的语言,提出所有权的概念,引入了极多符号:mut、&mut、ref mut、&、*、as_mut、as_ref。。。让人头秃。。。
之前看到过一句话,觉得很不错:学习 Rust 并不会给你带来智商上的优越感,但或许会让你重新爱上编程。
大家如果阅读过一些开源框架的源码,可能会发现其中数不尽的抽象类,设计模式拈手而来,在功能框架中,可以使用设计模式随心所欲的解耦;在实际的复杂业务中,当然也可以应用合适的设计模式。
这篇文章,我会将实际业务较为常见场景,如何合适将设计模式应用其中
本文章是一篇弱代码类型文章,我会画大量的图片向大家展示,引用设计模式后,会对原有的业务流程,产生什么样的影响。
前置知识
这里,需要了解下基础知识,什么是责任链模式和策略模式
责任链模式,在很多开源框架中都是有所应用,你如果听到啥啥拦截器,基本就是责任链模式,责任链模式的思想很简单,但是有很多种实现方式
最简单的链表实现就和 OkHttp 的拦截器实现大相径庭
OkHttp 的拦截器实现和 Dio 拦截器实现结构相同,但遍历方式不一样
很多骚操作:我喜欢 OkHttp 的实现方式,喜欢 dio 的 Api 设计,结尾会给出一个结合这俩者思想的通用拦截器
策略模式,或是天生适合业务,同一模块不同类型业务,如果行为相同,或许就可以考虑使用策略模式去解耦了
责任链模式
这边用 Dart 写一个简单的拦截器,dart 和 java 非常像
为了减少语言差异,我就不使用箭头语法了
下划线表示私有
用啥语言不重要,这边只是用代码简单演示下思想
此处实现就用链表了;如果,使用数组的形式,需要多写很多逻辑,数组的优化写法在结尾给出,此处暂且不表
结构
abstract class InterceptChain<T> {
InterceptChain? next;
void intercept(T data) {
next?.intercept(data);
}
}
复制代码
实现
/// 该拦截器以最简单的链表实现
abstract class InterceptChain<T> {
InterceptChain? next;
void intercept(T data) {
next?.intercept(data);
}
}
class InterceptChainHandler<T> {
InterceptChain? _interceptFirst;
void add(InterceptChain interceptChain) {
if (_interceptFirst == null) {
_interceptFirst = interceptChain;
return;
}
var node = _interceptFirst!;
while (true) {
if (node.next == null) {
node.next = interceptChain;
break;
}
node = node.next!;
}
}
void intercept(T data) {
_interceptFirst?.intercept(data);
}
}
复制代码
void main() {
var intercepts = InterceptChainHandler<String>();
intercepts.add(OneIntercept());
intercepts.add(TwoIntercept());
intercepts.intercept("测试拦截器");
}
class OneIntercept extends InterceptChain<String> {
@override
void intercept(String data) {
data = "$data:OneIntercept";
print(data);
super.intercept(data);
}
}
class TwoIntercept extends InterceptChain<String> {
@override
void intercept(String data) {
data = "$data:TwoIntercept";
print(data);
super.intercept(data);
}
}
复制代码
测试拦截器:OneIntercept
测试拦截器:OneIntercept:TwoIntercept
复制代码
策略模式
结构
实现
/// 结合适配器模式的接口适配:抽象必须实现行为,和可选实现行为
abstract class BusinessAction {
///创建相应资源:该行为必须实现
void create();
///可选实现
void dealIO() {}
///可选实现
void dealNet() {}
///可选实现
void dealSystem() {}
///释放资源:该行为必须实现
void dispose();
}
复制代码
//Net策略
class NetStrategy extends BusinessAction {
@override
void create() {
print("创建Net资源");
}
@override
void dealNet() {
print("创建IO资源");
}
@override
void dispose() {
print("创建Net资源");
}
}
///IO策略
class IOStrategy extends BusinessAction {
@override
void create() {
print("创建IO资源");
}
@override
void dealIO() {
print("处理IO逻辑");
}
@override
void dispose() {
print("释放IO资源");
}
}
复制代码
void main() {
var type = 1;
BusinessAction strategy;
//不同业务使用不同策略
if (type == 0) {
strategy = NetStrategy();
} else {
strategy = IOStrategy();
}
//开始创建资源
strategy.create();
//......... 省略N多逻辑(其中某些场景,会有用到Net业务,和上面type是关联的)
//IO业务:开始处理业务
strategy.dealIO();
//......... 省略N多逻辑
//释放资源
strategy.dispose();
}
复制代码
适合的业务场景
这边举一些适合上述设计模式的业务场景,这些场景是真实存在的!
这些真实的业务,使用设计模式解耦和纯靠 if else 怼,完全是俩种体验!
代码如诗,这并不是一句玩笑话。
连环弹窗业务
业务描述
连环弹窗夺命 call 来袭。。。
A 弹窗弹出:有确定和取消按钮
确定按钮:B 弹窗弹出(有查看详情和取消按钮)
查看详情按钮:C 弹窗弹出(有同意和拒绝按钮)
同意按钮:D 弹窗弹出(有查看和下一步按钮)
查看按钮:E 弹窗弹出(只有下一步按钮)
下一步按钮:F 弹窗弹出(结束)
下一步按钮:F 弹窗弹出(结束)
拒绝按钮:流程结束
取消按钮:流程结束
取消按钮:流程结束
好家伙,套娃真是无所不在,真不是我们代码套娃,实在是业务套娃,手动滑稽.jpng
直接开搞
看到这个业务,大家会去做呢?
产品来了,加需求
B 和 C 弹窗之间要加个预览 G 弹窗,点击 B 的查看详情按钮,跳转预览 G 弹窗,预览 G 弹窗只有一个确定按钮,跳转 C 弹窗
产品又来了,第一稿需求不合理,需要调整需求
交换 C 和 D 弹窗位置,D 弹窗点击下一步的时候,需要加一个校验请求,通过后才能跳转到 C 弹窗
回来,坐在椅子上,心里想:
老夫写的代码天衣无缝,这什么几把需求
可恶,这次测试,起码要给我多提十几个 BUG
产品飘来,加改需求:如此,如此,,,这般,这般,,,
产品:改下,,,然后,扔给你几十页的 PRD
重构
随着业务的逐渐复杂,最初的设计缺点会逐渐暴露;重构有缺陷的代码流程,变得势在必行,这会极大降低维护成本
如果心中对责任链模式有一些概念的话,会发现上面的业务,极其适合责任链模式!
对上面的业务进行分析,可以明确一些事
这个业务是一个链式的,有着较明确方向性,单向,从头到尾指向
业务拆分开,可以将一个弹窗作为颗粒度,一个弹窗作为节点
上级的业务节点可以对下级节点拦截(点击取消,拒绝按钮,不再进行后续业务)
重构上面的代码,只要明确思想和流程就行了
第一稿业务
void main() {
var intercepts = InterceptChainHandler<String>();
intercepts.add(AIntercept());
intercepts.add(BIntercept());
intercepts.add(CIntercept());
intercepts.add(DIntercept());
intercepts.add(EIntercept());
intercepts.add(FIntercept());
intercepts.intercept("测试拦截器");
}
复制代码
第二稿业务
void main() {
var intercepts = InterceptChainHandler<String>();
intercepts.add(AIntercept());
intercepts.add(BIntercept());
intercepts.add(GIntercept());
intercepts.add(CIntercept());
intercepts.add(DIntercept());
intercepts.add(EIntercept());
intercepts.add(FIntercept());
intercepts.intercept("测试拦截器");
}
复制代码
第三稿业务
void main() {
var intercepts = InterceptChainHandler<String>();
intercepts.add(AIntercept());
intercepts.add(BIntercept());
intercepts.add(GIntercept());
intercepts.add(DIntercept());
intercepts.add(CIntercept());
intercepts.add(EIntercept());
intercepts.add(FIntercept());
intercepts.intercept("测试拦截器");
}
复制代码
总结
经过责任链模式重构后,业务节点被明确的区分开,整个流程从代码上看,都相当的清楚,维护将变的异常轻松;或许,此时能感受到一些,编程的乐趣了
花样弹窗业务
业务描述
这边来描述一个业务,这个业务场景真实存在某办公软件
进入 APP 首页,然后和后台建立一个长连接
后台某些工单处理后,会通知 APP 处理,此时 app 会弹出处理工单的弹窗(app 顶部)
弹窗类型很多:工单处理弹窗,流程审批弹窗,邀请类型弹窗,查看工单详情弹窗,提交信息弹窗。。。
弹窗弹出类型,是根据后台给的 Type 进行判断:从而弹出不同类型弹窗、点击其按钮,跳转不同业务,传递不同参数。
分析
确定设计
这个业务,是一种渐变性的引导你搭建克苏鲁代码山
首先这个业务,使用责任链模式,肯定是不合适的,因为弹窗之间的耦合性很低,并没有什么明确的上下游关系
但是,这个业务使用策略模式非常的适合!
抽象行为
多样弹窗的行为抽象,对应其按钮就行了
确定、取消、同意、拒绝、查看详情、我知道了、提交
直接画图来表示吧
实现
来看下简要的代码实现,代码不重要,重要的是思想,这边简要的看下代码实现流程
/// 默认实现抛异常,可提醒未实现方法被误用
abstract class DialogAction {
///确定取消
void onConfirm() {
throw 'DialogAction:not implement onConfirm()';
}
///取消
void onCancel() {
throw 'DialogAction:not implement onCancel()';
}
///同意
void onAgree() {
throw 'DialogAction:not implement onAgree()';
}
///拒绝
void onRefuse() {
throw 'DialogAction:not implement onRefuse()';
}
///查看详情
void onDetail() {
throw 'DialogAction:not implement onDetail()';
}
///我知道了
void onKnow() {
throw 'DialogAction:not implement onKnow()';
}
///提交
void onSubmit() {
throw 'DialogAction:not implement onSubmit()';
}
}
复制代码
class OneStrategy extends DialogAction {
@override
void onConfirm() {
print("确定");
}
@override
void onCancel() {
print("取消");
}
}
class TwoStrategy extends DialogAction{
@override
void onAgree() {
print("同意");
}
@override
void onRefuse() {
print("拒绝");
}
}
//........省略其他实现
复制代码
void main() {
//根据接口获取
var type = 1;
DialogAction strategy;
switch (type) {
case 0:
strategy = DefaultStrategy();
break;
case 1:
strategy = OneStrategy();
break;
case 2:
strategy = TwoStrategy();
break;
case 3:
strategy = ThreeStrategy();
break;
case 4:
strategy = FourStrategy();
break;
case 5:
strategy = FiveStrategy();
break;
default:
strategy = DefaultStrategy();
break;
}
//聚合弹窗按钮触发事件
BusinessDialog(
//确定按钮
onConfirm: () {
strategy.onConfirm();
},
//取消按钮
onCancel: () {
strategy.onCancel();
},
//同意按钮
onAgree: () {
strategy.onAgree();
},
//拒绝按钮
onRefuse: () {
strategy.onRefuse();
},
//查看详情按钮
onDetail: () {
strategy.onDetail();
},
//我知道了按钮
onKnow: () {
strategy.onKnow();
},
//提交按钮
onSubmit: () {
strategy.onSubmit();
},
);
}
复制代码
一个复杂业务场景的演变
我们看下,一个简单的提交业务流,怎么逐渐变的狰狞
我会逐渐给出一个合适的解决方案,如果大家有更好的想法,务必在评论区告诉鄙人
业务描述:我们的车子因不可抗原因坏了,要去维修厂修车,工作人员开始登记这个损坏车辆。。。
业务的演变
第一稿
初始业务
登记一个维修车辆的流程,实际上还是满麻烦的
登记一个新车,需要将车辆详细信息登记清楚:车牌、车架、车型号、车辆类型、进出场时间、油量、里程。。。
还需要登记一下用户信息:姓名、手机号、是否隶属公司。。。
登记车损程度:车顶、车底、方向盘、玻璃、离合器、刹车。。。
车内物品:车座皮套、工具。。。
以及其他我没想到。。。
最后:提交所有登记好的信息
第一稿,业务流程十分清晰,细节复杂,但是做起来不难
第二稿(实际是多稿聚合):增加下述几个流程
外部登记:外部登记了一个维修车辆部分信息(后台,微信小程序,H5 等等),需要在 app 上完善信息,提交接口不同(必带车牌号)
快捷洗车:洗车业务极其常见,快捷生成对应信息,提交接口不同
预约订单登记:预约好了车辆一部分一些信息,可快捷登记,提交接口不同(必带车牌号)
因为登记维修车辆流程,登记车辆信息流程极其细致繁琐,我们决定复用登记新车模块
因为这一稿需求,业务也变得愈加复杂
第三稿
现在要针对不同的车辆类型,做不同的处理;车类型分:个人车,集团车
不同类型的登记,在提交的时候,需要校验不同的信息;校验不通过,需要提示用户,并且不能进行提交流程
提交完,处理下通用也通用业务,然后跳转的某个页面
第三稿的描述不多,但是,也同样大大的增加了复杂度
开发探讨
第一稿
正常流程开发、、、
第二稿
对于第二稿的业务,可以好好的思考,可以怎么去开发?
开头和结尾需要单独写判断,去处理不同流程的业务,这至少要写俩个大的判断模块,接受数据的入口模块可能还要写判断
这样就非常适合策略模式去做了
开头根据执行的流程,选择相应的策略对象,后续将逻辑块替换抽象的策略方法就 OK 了,大致流程如下
第三稿
业务流程
探讨
可以用责任链模式!需要做一些小改动!这地方,我们需要将频繁变动的模块用责任链模式全都隔离出来
浏览上述流程图可发现,本来是极度杂乱糅合的业务,可以被设计相对更加平行的结构
对于上述流程,可以进一步分析,并进一步简化:对整体业务分析,我们需要去关注其变或不变的部分
不变:整体业务变动很小的是,登记信息流程(主题逻辑这块),此处的相关变动是很小的,对所有流程也是共用的部分
变:可以发现,开头和结尾是变动更加频繁的部分,我们可以对此处逻辑进行整体的抽象
抽象多变的开头和结尾
abstract class InterceptChainTwice<T> {
InterceptChainTwice? next;
void onInit(T data) {
next?.onInit(data);
}
void onSubmit(T data) {
next?.onSubmit(data);
}
}
复制代码
来看下简要的代码实现,代码不重要,主要看看实现流程和思想
abstract class InterceptChainTwice<T> {
InterceptChainTwice? next;
void onInit(T data) {
next?.onInit(data);
}
void onSubmit(T data) {
next?.onSubmit(data);
}
}
class InterceptChainTwiceHandler<T> {
InterceptChainTwice? _interceptFirst;
void add(InterceptChainTwice interceptChain) {
if (_interceptFirst == null) {
_interceptFirst = interceptChain;
return;
}
var node = _interceptFirst!;
while (true) {
if (node.next == null) {
node.next = interceptChain;
break;
}
node = node.next!;
}
}
void onInit(T data) {
_interceptFirst?.onInit(data);
}
void onSubmit(T data) {
_interceptFirst?.onSubmit(data);
}
}
复制代码
/// 开头通用拦截器
class CommonIntercept extends InterceptChainTwice<String> {
@override
void onInit(String data) {
//如果有车牌,请求接口,获取数据
//.................
//填充页面
super.onInit(data);
}
}
/// 登记新车拦截器
class RegisterNewIntercept extends InterceptChainTwice<String> {
@override
void onInit(String data) {
//处理开头针对登记新车的单独逻辑
super.onInit(data);
}
@override
void onSubmit(String data) {
var isPass = false;
//如果校验不过,拦截下游逻辑
if (!isPass) {
return;
}
// ......
super.onSubmit(data);
}
}
/// 省略其他实现
复制代码
void main() {
var type = 0;
var intercepts = InterceptChainTwiceHandler();
intercepts.add(CommonIntercept());
intercepts.add(CarTypeDealIntercept());
if (type == 0) {
//登记新车
intercepts.add(CommonIntercept());
} else if (type == 1) {
//外部登记
intercepts.add(OutRegisterIntercept());
} else if (type == 2) {
//快捷洗车
intercepts.add(FastWashIntercept());
} else {
//预约订单登记
intercepts.add(OrderRegisterIntercept());
}
intercepts.add(TailIntercept());
//业务开始
intercepts.onInit("传入数据源");
//开始处理N多逻辑
//............................................................
//经历了N多逻辑
//提交按钮触发事件
SubmitBtn(
//提交按钮
onSubmit: () {
intercepts.onSubmit("传入提交数据");
},
);
}
复制代码
总结
关于代码部分,关键的代码,我都写出来,用心看看,肯定能明白我写的意思
也不用找我要完整代码了,这些业务 demo 代码写完后,就删了
本栏目这个业务,实际上是非常常见的的一个业务,一个提交流程与很多其它的流程耦合,整个业务就会慢慢的变的鬼畜,充满各种判断,很容易让人陷入泥泞,或许,此时可以对已有业务进行思考,如何进行合理的优化
该业务的演变历程,和开发改造是本人的一次思路历程,如大家有更好的思路,还请不吝赐教。
通用拦截器
我结合 OkHttp 的思想和 Dio 的 API,封装了俩个通用拦截器,这边贴下代码,如果哪里有什么不足,请及时告知本人
说明下:这是 Dart 版本的
抽象单方法
///一层通用拦截器,T的类型必须一致
abstract class InterceptSingle<T> {
void intercept(T data, SingleHandler handler) => handler.next(data);
}
///添加拦截器,触发拦截器方法入口
class InterceptSingleHandler<T> {
_InterceptSingleHandler _handler = _InterceptSingleHandler(
index: 0,
intercepts: [],
);
void add(InterceptSingle intercept) {
//一种类型的拦截器只能添加一次
for (var item in _handler.intercepts) {
if (item.runtimeType == intercept.runtimeType) {
return;
}
}
_handler.intercepts.add(intercept);
}
void delete(InterceptSingle intercept) {
_handler.intercepts.remove(intercept);
}
void intercept(T data) {
_handler.next(data);
}
}
///------------实现不同处理器 参照 dio api设计 和 OkHttp实现思想---------------
abstract class SingleHandler {
next(dynamic data);
}
///实现init处理器
class _InterceptSingleHandler extends SingleHandler {
List<InterceptSingle> intercepts;
int index;
_InterceptSingleHandler({
required this.index,
required this.intercepts,
});
@override
next(dynamic data) {
if (index >= intercepts.length) {
return;
}
var intercept = intercepts[index];
var handler =
_InterceptSingleHandler(index: index + 1, intercepts: intercepts);
intercept.intercept(data, handler);
}
}
复制代码
抽象双方法
///俩层通用拦截器,T的类型必须一致
abstract class InterceptTwice<T> {
void onInit(T data, TwiceHandler handler) => handler.next(data);
void onSubmit(T data, TwiceHandler handler) => handler.next(data);
}
///添加拦截器,触发拦截器方法入口
class InterceptTwiceHandler<T> {
_TwiceInitHandler _init = _TwiceInitHandler(index: 0, intercepts: []);
_TwiceSubmitHandler _submit = _TwiceSubmitHandler(index: 0, intercepts: []);
void add(InterceptTwice intercept) {
//一种类型的拦截器只能添加一次
for (var item in _init.intercepts) {
if (item.runtimeType == intercept.runtimeType) {
return;
}
}
_init.intercepts.add(intercept);
_submit.intercepts.add(intercept);
}
void delete(InterceptTwice intercept) {
_init.intercepts.remove(intercept);
_submit.intercepts.remove(intercept);
}
void onInit(T data) {
_init.next(data);
}
void onSubmit(T data) {
_submit.next(data);
}
}
///------------实现不同处理器 参照 dio api设计 和 OkHttp实现思想---------------
abstract class TwiceHandler {
next(dynamic data);
}
///实现init处理器
class _TwiceInitHandler extends TwiceHandler {
List<InterceptTwice> intercepts;
int index;
_TwiceInitHandler({
required this.index,
required this.intercepts,
});
@override
next(dynamic data) {
if (index >= intercepts.length) {
return;
}
var intercept = intercepts[index];
var handler = _TwiceInitHandler(index: index + 1, intercepts: intercepts);
intercept.onInit(data, handler);
}
}
///实现submit处理器
class _TwiceSubmitHandler extends TwiceHandler {
List<InterceptTwice> intercepts;
int index;
_TwiceSubmitHandler({
required this.index,
required this.intercepts,
});
@override
next(dynamic data) {
if (index >= intercepts.length) {
return;
}
var intercept = intercepts[index];
var handler = _TwiceSubmitHandler(index: index + 1, intercepts: intercepts);
intercept.onSubmit(data, handler);
}
}
复制代码
最后
第一次,写这种结合业务的文章
如有收获,还请点个赞,让我感受到你读有所获~~~~
感谢阅读,下次再会~~
评论 (1 条评论)