Preface
I haven't written an article for a while. I've been addicted to Rust recently, and I can't get rid of it. Rust is poisonous; this is really a very interesting language, and the design in many places really satisfies all my yearnings.
Of course, this is not a simple language. It puts forward the concept of ownership and introduces a lot of symbols: mut, &mut, ref mut, &, *, as_mut, as_ref. . . Make people bald. . .
I have read a sentence before, and I think it is very good: Learning Rust will not bring you superiority in IQ, but it may make you fall in love with programming again.
If you have read the source code of some open source frameworks, you may find countless abstract classes and design patterns. In the functional framework, you can use design patterns to decouple as you want; in actual complex business, of course Appropriate design patterns can also be applied.
In this article, I will combine more common actual business scenarios to discuss how to use appropriate design patterns to decouple business
- The application here is by no means a rigid method, it is a set of effective ideas that I have obtained after careful consideration and a comprehensive restructuring of the more complex business.
- Any design pattern is a great experience and a summary of its thoughts. There are thousands of people. If you have different opinions on the content of the article, I hope you can put it forward in the comments. We will discuss and make progress together.
This article is a weak code type article, I will draw a lot of pictures to show you how to use the design pattern, what impact will it have on the original business process.
Pre-knowledge
Here, you need to understand the basics, what is the responsibility chain model and strategy model
The chain of responsibility model is applied in many open source frameworks. If you hear about interceptors, it is basically the chain of responsibility model. The idea of the chain of responsibility model is very simple, but there are many ways to implement it.
- The simplest linked list implementation is very different from OkHttp's interceptor implementation
- OkHttp's interceptor implementation is the same as the Dio interceptor implementation structure, but the traversal method is different
- Many Sao operations: I like the implementation of OkHttp and the Api design of dio. At the end, a universal interceptor that combines the ideas of the two will be given.
Strategy mode, or is inherently suitable for business, different types of business in the same module, if the behavior is the same, maybe you can consider using the strategy mode to decouple
Chain of Responsibility Model
Here we use Dart to write a simple interceptor, dart and java are very similar to
- In order to reduce language differences, I do not use arrow syntax
- underlined to indicate private
used for 16136e4ddd23d1 is not important, here is just a simple demonstration of the idea with code
The implementation here uses a linked list; if you use the form of an array, you need to write a lot of logic. The optimized wording of the array is given at the end, and it will not be shown here for the time being
structure
The structure of the chain of responsibility usually has two structures
- Linked list structure: Linked list builds a chain of responsibility, which is very convenient to establish contact with the next node
- Array structure: Array, just use a general List, which is convenient for adding and deleting, not fixed length (don't bother to use fixed length Array, for example: int[], String[])
- Realizing a linked list entity is very simple
abstract class InterceptChain<T> {
InterceptChain? next;
void intercept(T data) {
next?.intercept(data);
}
}
accomplish
- Interceptor implementation
/// 该拦截器以最简单的链表实现
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);
}
}
use
- Adjust the order of add to adjust the order of the corresponding logical nodes in the entire chain of responsibility
- Remove the super.intercept(data) in the intercept override method to achieve interception of subsequent node logic
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);
}
}
- Print result
测试拦截器:OneIntercept
测试拦截器:OneIntercept:TwoIntercept
Strategy mode
structure
- The most important thing about the strategy pattern: it should be the design of abstract classes and the abstraction of behavior
accomplish
- Define abstract class, abstract behavior
/// 结合适配器模式的接口适配:抽象必须实现行为,和可选实现行为
abstract class BusinessAction {
///创建相应资源:该行为必须实现
void create();
///可选实现
void dealIO() {}
///可选实现
void dealNet() {}
///可选实现
void dealSystem() {}
///释放资源:该行为必须实现
void dispose();
}
- Implementation strategy class
//Net策略
class NetStrategy extends BusinessAction {
@override
void create() {
print("创建Net资源");
}
@override
void dealNet() {
print("处理Net逻辑");
}
@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资源");
}
}
- use
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();
}
- result
创建IO资源
处理IO逻辑
释放IO资源
Suitable business scenarios
Here are some business scenarios suitable for the above design patterns, these scenarios are real!
These real businesses, decoupling using design patterns and relying solely on if else, are completely two experiences!
code of 16136e4ddd2a58 is poetic, this is not a joke.
Serial pop-up business
Business description
A serial pop-up window struck a life-threatening call. . .
A pop-up window pops up: there are OK and Cancel buttons
OK button: B pop-up window pops up (with view details and cancel buttons)
View details button: C pop-up window pops up (with agree and reject buttons)
Agree button: D pop-up window pops up (there is a view and next button)
View button: E pop-up window pops up (only the next button)
- Next button: F pop-up window pops up (end)
- Next button: F pop-up window pops up (end)
- Reject button: end of process
- Cancel button: end of process
- Cancel button: end of process
Good guy, dolls are really ubiquitous, it’s not our code dolls, it’s business dolls, manual funny.png
- Icon pop-up business
Start directly
Seeing this business, what will everyone do?
Someone might think, do we still need to think about such a simple business? Write directly!
- A: In the confirm callback, jump to the B pop-up window
- B: View details button to jump to C pop-up window
- 。。。
- After finishing the set, I finally finished writing
products are here, add demand
A preview G pop-up window should be added between B and C pop-up windows. Click the view details button of B to jump to the preview G pop-up window; the preview G pop-up window has only one OK button, click it to jump to the C pop-up window
You may be thinking about it, isn't this really cheating?
- The business is already super-Jill. The jump code written in the pop-up window of my B needs to be changed, the transfer parameter needs to be changed, and the pop-up window needs to be added!
First go to find the product tearing ratio, after tearing it
- Then continue on the shit hill, carefully pulling the shit again
- The initial scale of this Cthulhu Mountain
product is here again, the first draft demand is unreasonable, and the demand needs to be adjusted
swaps the positions of C and D pop-up windows, logic adjustment: when the D pop-up window clicks next, you need to add a verification request, after passing, jump to the C pop-up window, click the view button to jump to the pop-up window F
You frown and realize that things are not as simple as the surface
- Since the initial drawings are simple, they are almost all written in one file, so there are too many pop-up callbacks, and the pop-up styles are also different.
- Now change the whole process, causing your whole brain to buzz
- I was so angry that I found the product and said
When I came back, I sat in a chair and thought to myself:
- The code written by the old man is seamless. What are the requirements?
- Damn, this test, at least I have to mention a dozen more bugs
- Cthulhu Mountain begins to be ferocious
products are coming, add and change demand: so, so,,, so, so,,,,
- you....
product: change,,, and then throw you dozens of pages of PRD
You look at this revised version of Cthulhu Mountain, and the logic of these dozens of pop-up windows is actually written in one file, almost 10,000 lines of code. . .
I can't help thinking:
- The code written by Ben Shuaibi is really amazing, maybe this is art! Art is always high and low, hard to be understood! And my code is even more impressive, even I can't understand it myself!
- This number of lines of code! This code structure! Don't take a photo for memorialization, and pass it on to future children as a family heirloom!
I can't help but cry:
- In this business, besides me, anyone who dares to move and become the boss's confidant is just around the corner!
- But, after thinking about it again and again: when things are over, I will leave my clothes and hide my merits and fame deeply.
Refactor
As the business becomes more complex, the initial design shortcomings will gradually be exposed; refactoring the defective code flow becomes imperative, which will greatly reduce maintenance costs
If you have some concepts about the responsibility chain model in your mind, you will find that the above business is extremely suitable for the responsibility chain model!
Analysis of the above business can clarify some things
- This business is a chain, with a clear direction: one-way, from the beginning to the end
- Business is split, one pop-up window can be used as a single granularity, and one pop-up window can be used as a node
- The upper-level business node can intercept the lower-level node (click the cancel, reject button, and no further business will be performed)
Refactor the above code, as long as the thought and process are clear
First draft business
- Business Process
- Chain of responsibility
- Code: shorthand
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("测试拦截器");
}
Second draft business
- Business Process
- Chain of responsibility
- Code: shorthand
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("测试拦截器");
}
third draft business
- Business Process
- Chain of responsibility
- Code: shorthand
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("测试拦截器");
}
Summary
After refactoring the responsibility chain model, the business nodes are clearly distinguished. The entire process is quite clear from the code point of view, and maintenance will become extremely easy; perhaps, at this time, I can feel some of the fun of programming.
Pattern pop-up business
Business description
To describe a new business: this business scenario really exists in a certain office software
- After entering the APP homepage, establish a long connection with the background
- After some work orders are processed in the background, the APP will be notified to process. At this time, the app will pop up a pop-up window for processing the work order (top of the app)
- There are many types of pop-up windows: ticket processing pop-up window, process approval pop-up window, invitation-type pop-up window, pop-up window for viewing ticket details, and pop-up window for submitting information. . .
- The pop-up type of the pop-up window is judged according to the Type given in the background: different types of pop-up windows will pop up, click its button, jump to different businesses, and pass different parameters.
analyze
Determine the design
This business is a gradual guide to build the Cthulhu Code Mountain
- In the early stage of development, there are generally only two or three types of pop-up windows, which are very easy to do in the early stage; there is no need to consider how to design, just raise one line of code and backhand one line of code to get it done
- But then the entire business will gradually become ghosts, and different types will gradually increase to dozens of them! ! !
First of all, for this business, it is definitely inappropriate to use the chain of responsibility model, because the coupling between the pop-up windows is very low, and there is no clear upstream and downstream relationship.
However, this business uses a strategic model very suitable!
- Type is clear: different types of pop-up windows pop up, buttons perform different logic
- The abstract behavior is clear: a button is a behavior, and the implementation logic of different behaviors is very different
abstract behavior
The behavior of various pop-up windows is abstract, just correspond to the button
confirm, cancel, agree, reject, view details, I see, submit
Just draw a picture to show it
accomplish
Let’s take a look at the brief code implementation, the code is not important, the important thing is the thought, here is a brief look at the code implementation process
- Abstract base class
/// 默认实现抛异常,可提醒未实现方法被误用
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()';
}
}
- Implement logic class
class OneStrategy extends DialogAction {
@override
void onConfirm() {
print("确定");
}
@override
void onCancel() {
print("取消");
}
}
class TwoStrategy extends DialogAction{
@override
void onAgree() {
print("同意");
}
@override
void onRefuse() {
print("拒绝");
}
}
//........省略其他实现
- use
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;
}
//聚合弹窗按钮触发事件(不同弹窗的确定按钮,皆可聚合为一个onConfirm事件,其它同理)
BusinessDialog(
//通过传入的type,显示对应类型的弹窗
type: type,
//确定按钮
onConfirm: () {
strategy.onConfirm();
},
//取消按钮
onCancel: () {
strategy.onCancel();
},
//同意按钮
onAgree: () {
strategy.onAgree();
},
//拒绝按钮
onRefuse: () {
strategy.onRefuse();
},
//查看详情按钮
onDetail: () {
strategy.onDetail();
},
//我知道了按钮
onKnow: () {
strategy.onKnow();
},
//提交按钮
onSubmit: () {
strategy.onSubmit();
},
);
}
- Icon
The evolution of a complex business scenario
Let’s take a look at how a simple submission business flow gradually becomes hideous
I will gradually give a suitable solution, if you have a better idea, be sure to tell me in the comment area
. We have to go to the repair shop to repair the car. The staff began to register the damaged car. . .
Business evolution
First draft
Initial business
The process of registering a vehicle for maintenance is actually quite troublesome
- To register a new car, you need to register the detailed vehicle information: license plate, frame, car model, vehicle type, entry and exit time, fuel volume, and mileage. . .
- You also need to register user information: name, mobile phone number, and whether you belong to the company. . .
- Register the degree of car damage: roof, bottom, steering wheel, glass, clutch, brake. . .
- In-car items: seat holsters, tools. . .
- And other things I didn't expect. . .
- Finally: Submit all registered information
The first draft, the business process is very clear and the details are complicated, but it is not difficult to do
second draft (actually multiple drafts): add the following processes
External registration: Part of the information of a repaired vehicle (backstage, WeChat applet, H5, etc.) is registered externally, and the information needs to be completed on the app, and the submission interface is different (the license plate number must be included)
Quick car wash: car wash business is extremely common, the corresponding information is quickly generated, and the submission interface is different
Appointment order registration: if you have reserved some information about the vehicle, you can quickly register, and the submission interface is different (the license plate number must be included)
Because the process of registering and maintaining a vehicle, the process of registering vehicle information is extremely detailed and cumbersome, we decided to reuse the registration new car module
- Because most of the logic here involves the beginning and the end, the operation of registering vehicle information in the middle is almost unchanged, and the reuse idea is feasible
- If vehicle registration items are added, the new three processes must also submit this information; therefore, reuse is imperative
Because of the demand for this draft, the business has become more complicated
Third Draft
Now we need to do different treatments for different vehicle types; car types are divided into: personal car, group car
Different types of registration need to be verified when submitting different information; if the verification fails, the user needs to be prompted, and the submission process cannot be performed
submitted, you need to process the general business, and then jump to a certain page
The description of the third draft is not much, but it greatly increases the complexity
- Especially the different types of verification process are different, and it can also interrupt the subsequent submission process.
- After submitting the process, you also need to jump to the general page
Development Discussion
first draft
- Business Process
- Development
Normal process development,,,
Second draft
- Business Process
- think
For the second draft business, you can think about it carefully. How to design it?
At the beginning and the end, you need to write judgments separately to handle the business of different processes. This requires at least two large judgment modules. The entry module that accepts the data may also need to write judgments.
This is very suitable for strategic mode to do
At the beginning, select the corresponding strategy object according to the execution process, and then replace the logic block with the abstract strategy method. The general process is as follows
Third draft
business process
explore
The requirements of the third draft are actually more complicated
- The entire process is mixed with different business process processing, and different process logic has a blocking downstream mechanism (green module)
- Downstream logic will converge (end) multiple transformations
Demand in this draft
- It is definitely possible to use the strategy mode
- The block (green module) needs to be processed separately: The
abstract method should have a return value, and the outer layer judges whether to proceed with the subsequent process based on the return value
- but! this! It's too inelegant!
Consider some of the characteristics of the above business
- Intercept downstream mechanisms
- From upstream to downstream, the direction is clear
- New business processes may be inserted at any time. . .
You can use the chain of responsibility model! However, some small changes need to be made! In this place, we can isolate all frequently changing modules using the chain of responsibility model
- Take a look at the flow chart after the transformation using the chain of responsibility model
Looking at the above flow chart, you can find that the business that was originally extremely messy and mixed can be designed with a relatively more parallel structure.
The above process can be further analyzed and simplified: for the overall business analysis, we need to pay attention to the changed or unchanged parts
- Unchanged: The small change in the overall business is the registration information process (the main logic part). The related changes here are very small, and it is also a common part for all processes.
- Change: It can be found that the beginning and the end are the parts that change more frequently, and we can abstract the logic here as a whole
- Abstract and changeable beginning and end
- So we abstract the interception class, we can make some adjustments
abstract class InterceptChainTwice<T> {
InterceptChainTwice? next;
void onInit(T data) {
next?.onInit(data);
}
void onSubmit(T data) {
next?.onSubmit(data);
}
}
Let’s take a look at the brief code implementation, the code is not important, we mainly look at the implementation process and ideas
- Abstract interceptor
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);
}
}
- Implement interceptor
/// 开头通用拦截器
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);
}
}
/// 省略其他实现
- use
void main() {
var type = 0;
var intercepts = InterceptChainTwiceHandler();
intercepts.add(CommonIntercept());
intercepts.add(CarTypeDealIntercept());
if (type == 0) {
//登记新车
intercepts.add(RegisterNewCarIntercept());
} 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("传入提交数据");
},
);
}
Summarize
Regarding the code part, I have written all the key codes, and after a careful look, I will definitely understand what I am writing.
You don’t need to ask me for the complete code anymore. After the business demo code is written, I delete it.
The business of this column is actually a very common business. A submission process is coupled with many other processes, and the entire business will slowly become a ghost, full of various judgments, and it is easy for people to fall into the mud. Perhaps, At this point, you can think about the existing business and how to optimize it reasonably
The evolution of the business and the development and transformation are my own thoughts. If you have better ideas, please feel free to let me know.
Universal interceptor
I combined OkHttp's ideas and Dio's API to encapsulate two general interceptors. Here are the codes. If there are any shortcomings, please inform me in time.
Explanation: This is the Dart version
Abstract single method
///一层通用拦截器,T的类型必须一致
abstract class InterceptSingle<T> {
void intercept(T data, SingleHandler handler) => handler.next(data);
}
///添加拦截器,触发拦截器方法入口
class InterceptSingleHandler<T> {
_InterceptSingleHandler _handler = _InterceptSingleHandler(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 {
/// span: 设置该参数,可控跨越多级节点
/// 默认0,则不跨越节点(遍历所有节点)
next(dynamic data, {int span = 0});
}
///实现init处理器
class _InterceptSingleHandler extends SingleHandler {
List<InterceptSingle> intercepts;
int index;
_InterceptSingleHandler({
this.index = 0,
required this.intercepts,
});
@override
next(dynamic data, {int span = 0}) {
if ((index + span) >= intercepts.length) return;
var intercept = intercepts[index + span];
var handler = _InterceptSingleHandler(
index: index + (span + 1),
intercepts: intercepts,
);
intercept.intercept(data, handler);
}
}
Abstract double method
///俩层通用拦截器,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(intercepts: []);
_TwiceSubmitHandler _submit = _TwiceSubmitHandler(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 {
/// span: 设置该参数,可控跨越多级节点
/// 默认0,则不跨越节点(遍历所有节点)
next(dynamic data, {int span = 0});
}
///实现init处理器
class _TwiceInitHandler extends TwiceHandler {
List<InterceptTwice> intercepts;
int index;
_TwiceInitHandler({
this.index = 0,
required this.intercepts,
});
@override
next(dynamic data, {int span = 0}) {
if ((index + span) >= intercepts.length) return;
var intercept = intercepts[index + span];
var handler = _TwiceInitHandler(
index: index + (span + 1),
intercepts: intercepts,
);
intercept.onInit(data, handler);
}
}
///实现submit处理器
class _TwiceSubmitHandler extends TwiceHandler {
List<InterceptTwice> intercepts;
int index;
_TwiceSubmitHandler({
this.index = 0,
required this.intercepts,
});
@override
next(dynamic data, {int span = 0}) {
if ((index + span) >= intercepts.length) {
return;
}
var intercept = intercepts[index + span];
var handler = _TwiceSubmitHandler(
index: index + (span + 1),
intercepts: intercepts,
);
intercept.onSubmit(data, handler);
}
}
finally
For the first time, writing this kind of combined business article
If you have gained something, please give me a thumbs up and let me feel whether you have gained something from reading~~
Thanks for reading, see you next time~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。