preface

I haven’t written in a while. I’m addicted to Rust and can’t extricate myself. It’s really a very interesting language, and the design of many places really satisfies all my desires.

Of course, this is not a simple language, introducing the concept of ownership and numerous symbols: mut, &mut, ref mut, &, *, AS_mut, AS_ref… Bald heads…

Learning Rust will not make you intellectually superior, but it may make you love programming again.

If you read the source code of some open source frameworks, you may find that there are countless abstract classes, design patterns come at a fingertips, and in the functional framework, you can use design patterns to decouple as you like; Of course, appropriate design patterns can also be applied in real complex businesses.

In this article, I will explore how to use appropriate design patterns to decouple the business using more common real-world business scenarios

  • The application here is by no means mechanically applied. It is a set of effective thinking process obtained by me after careful consideration and comprehensive reconstruction of complex business
  • Any design mode is a great experience and summary of ideas, thousands of people, if you have different opinions on the content of the article, I hope you can put forward in the comments, we discuss together, common progress

This article is a weak code type article, and I will draw a lot of pictures to show you how the introduction of design patterns can affect the original business process.

Front knowledge

Here, we need to understand the basic knowledge, what is the chain of responsibility mode and strategy mode

The chain of responsibility model is used 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 simple, but there are many ways to implement it

  • The simplest implementation of a linked list is very different from OkHttp’s interceptor implementation
  • OkHttp’s interceptor implementation has the same structure as Dio’s interceptor implementation, but traverses differently
  • Lots of tricks: I like the OkHttp implementation, I like dio’S Api design, and I’ll end with a generic interceptor that combines both ideas

A strategic pattern, or one that is naturally suited to the business, might be considered for decoupling if the same module has the same behavior for different types of business

Chain of Responsibility model

Write a simple interceptor here using Dart, which is very similar to Java

  • In order to reduce language differences, I will not use arrow syntax
  • The underscore means private

It doesn’t matter what language you use, but I’m just going to show you the idea in code

This implementation uses a linked list; If you use an array, you need to write a lot more logic. The optimized way to write an array is given at the end

structure

  • There are usually two structures in the chain of responsibility
    • Linked list structure: Linked lists create a chain of responsibilities that can be easily linked to the next node
    • Int [], String[], int[], int[], int[], int[], int[], int[]

  • Implementing a linked list entity is simple
abstract class InterceptChain<T> {
  InterceptChain? next;

  void intercept(T data) {
    next?.intercept(data);
  }
}
Copy the code

implementation

  • Interceptor implementation
/// The interceptor is implemented as a simple linked list
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;
    }

    varnode = _interceptFirst! ;while (true) {
      if (node.next == null) {
        node.next = interceptChain;
        break;
      }
      node = node.next!;
    }
  }

  void intercept(T data) {
    _interceptFirst?.intercept(data);
  }
}
Copy the code
  • use
    • By adjusting the order of add, you adjust the order of the corresponding logical nodes in the responsibility chain
    • Intercept (data) is removed from the intercept override method to intercept subsequent node logic
void main() {
  var intercepts = InterceptChainHandler<String> (); intercepts.add(OneIntercept()); intercepts.add(TwoIntercept()); intercepts.intercept("Test interceptor");
}

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); }}Copy the code
  • Print the result
Test interceptor: OneIntercept Test interceptor: OneIntercept: TwoInterceptCopy the code

The strategy pattern

structure

  • The most important thing about policy patterns is the design of abstract classes, the abstraction of behavior

implementation

  • Define abstract classes, abstract behavior
/// Interface adaptation with adapter patterns: Abstraction must implement behavior, and optional implementation behavior
abstract class BusinessAction {
  ///Create resources: This behavior must be implemented
  void create();

  ///The optional implementation
  void dealIO() {}

  ///The optional implementation
  void dealNet() {}

  ///The optional implementation
  void dealSystem() {}

  ///Release resources: This action must be implemented
  void dispose();
}
Copy the code
  • Implementation Policy class
/ /.net strategy
class NetStrategy extends BusinessAction {
  @override
  void create() {
    print("Create Net resource");
  }

  @override
  void dealNet() {
    print("Handling Net logic");
  }

  @override
  void dispose() {
    print("Free Net resources"); }}///IO strategy
class IOStrategy extends BusinessAction {
  @override
  void create() {
    print("Create IO resource");
  }

  @override
  void dealIO() {
    print("Handle IO logic");
  }

  @override
  void dispose() {
    print("Release IO resources"); }}Copy the code
  • use
void main() {
  var type = 1;
  BusinessAction strategy;

  // Different services use different policies
  if (type == 0) {
    strategy = NetStrategy();
  } else {
    strategy = IOStrategy();
  }

  // Start creating resources
  strategy.create();
  / /... Omit N more logic (some scenarios are useful for Net business and are associated with type above)
  //IO service: starts processing services
  strategy.dealIO();
  / /... Omit N more logic
  // Release resources
  strategy.dispose();
}
Copy the code
  • The results of
Create I/O resources process I/O logic and release I/O resourcesCopy the code

Suitable business scenarios

Here are some business scenarios that fit the above design patterns. These scenarios are real!

These real businesses, using design pattern decoupling and pure if else hatred, are two very different experiences!

Code is poetry, and this is not a joke.

Serial popover service

Business description

Call…

  • A Pop-up window: There are confirm and cancel buttons
    • Confirm button: B popup window (with view details and cancel buttons)
      • View details button: C popup window (with Agree and reject buttons)
        • Agree button: D popup window (with view and Next buttons)
          • View button: E popup window pops up (only next button)
            • Next button: F popup window (end)
          • Next button: F popup window (end)
        • Reject button: the process ends
      • Cancel button: the process ends
    • Cancel button: the process ends

Good guy, nesting doll is really everywhere, really not our code nesting doll, it is the business nesting doll, manual funny. PNG

  • Icon popover business

Direct drive to

What do people do when they see this business?

  • Some people may think, such a simple business need to think about? Just write it!
    • A: In the sure callback, jump to the B popup
    • B: View details to jump to C pop-up window
    • .
  • After a lot of hoodwinking, I finally finished

Product comes, add demand

Add a preview G popover between B and C popovers. Click the view details button of B to jump to preview G popover. Preview G popup has only one “OK” button. Click it to jump to C popup

  • And you’re probably thinking, isn’t that cheating?
    • Business was super Gil set, I B popover inside write jump code to change, transfer to change, but also add popover!
  • To find a product to tear than, after tearing
    • Then continue on the mountain of shit, carefully pull shit again
    • This Cthulhu mountain is in its infancy

Here comes the product again. The demand for the first draft is unreasonable, so we need to adjust the demand

Switch the position of C and D pop-ups and adjust the logic: when clicking next in D popup, a verification request needs to be added. After passing through, it will jump to C popup and click view button to jump to F popup

  • You frown and realize there’s more to it than meets the eye
    • Because the initial image is simple, almost all written in a file, too many dazzling pop-up callbacks, and popovers style is different
    • Now change the whole process, cause your whole brain to buzz
  • In the heart of anger, find the product said

  • Come back, sit in your chair, and think to yourself,
    • My husband’s code is seamless. What are the requirements
    • Damn it, this test, at least give me a dozen more bugs

  • Cthulhu is starting to get ugly

Products come, add demand: so, so, so, so.

  • You…

Product: Revise… and throw you dozens of pages of PRD

Look at the dozens of versions of Cthulhu, the dozens of pop-up logic is written in a file, nearly 10,000 lines of code…

  • I couldn’t help thinking:
    • Ben shuai wrote the code really cool, maybe this is art! Art is always obscure and difficult to understand! And my code is more awesome, even I can’t understand!
    • Lines of code! This code structure! Can not take a picture to remember, pass to later children when heirloom for!
  • I couldn’t help but tell myself:
    • This business, except me, who else dare to move, become the chief’s confidant, just around the corner!

  • But, after turning to think deeply: thing brushed clothes go, deep work and name

refactoring

As the business gets more complex, initial design weaknesses are exposed; Refactoring defective code flows becomes imperative, which greatly reduces maintenance costs

If you have some concept of responsibility chain mode in mind, you will find that the above business is extremely suitable for the responsibility chain mode!

An analysis of the above business makes a few things clear

  • The business is a chain, with a clear direction: one way, from beginning to end
  • When services are separated, one popover can be used as a single granularity and one popover as a node
  • Upper-level business nodes can intercept lower-level nodes (click cancel and reject button to stop subsequent business)

To refactor the code above, just clarify the idea and flow

First draft business

  • The 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("Test interceptor");
}
Copy the code

Second draft business

  • The 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("Test interceptor");
}
Copy the code

Third Draft Business

  • The 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("Test interceptor");
}
Copy the code

conclusion

After the chain of responsibility mode reconstruction, business nodes are clearly separated, the whole process from the code, are quite clear, maintenance will become extremely easy; Maybe it’s time to feel some of the fun of programming

Fancy popover service

Business description

To describe a new business scenario: this business scenario actually exists some office software

  • After entering the APP home page, establish a long connection with the background
  • After some work orders are processed in the background, APP will be notified to process them. At this time, APP will pop up a popup window for processing work orders (top of APP).
  • There are many types of pop-ups: work order processing, process approval, invitation type, View work order details, submit information…
  • The popup Type can be judged according to the Type given by the background: pop-ups of different types can be popped up and buttons can be clicked to jump to different services and transfer different parameters.

Analysis of the

To determine the design

This business is a gradual guide to building cthulhu Code Mountain

  • In the early development, there are generally only two or three types of popovers, which are very easy to do in the early stage; You don’t even have to think about how to design it, you can put one line of code up, one line of code back, you can do it, right
  • But later the whole business will gradually ghost livestock, different types will be slowly added to dozens of kinds!!

First of all, it is definitely inappropriate to use the responsibility chain mode for this business, because the coupling between popovers is very low, and there is no clear upstream and downstream relationship

However, this business uses the policy pattern very well!

  • The type is clear: Different pop-ups are displayed for different types, and buttons perform different logic
  • Abstract behavior is explicit: a button is an action, and the implementation logic for different actions is very different

Abstract behavior

Multiple popovers behave abstractly, corresponding to their buttons

Confirm, cancel, agree, reject, view details, I got it, submit

Let me just draw it

implementation

Let’s take a look at the brief code implementation, the code is not important, the important is the idea, here is a brief look at the code implementation process

  • Abstract base class
/// The default implementation throws exceptions to alert unimplemented methods to misuse
abstract class DialogAction {
  ///determine
  void onConfirm() {
    throw 'DialogAction: Not Implement onConfirm()';
  }

  ///cancel
  void onCancel() {
    throw 'DialogAction: Not Implement onCancel()';
  }

  ///agree
  void onAgree() {
    throw 'DialogAction: Not Implement onAgree()';
  }

  ///Refused to
  void onRefuse() {
    throw 'DialogAction: Not implement onRefuse()';
  }

  ///Check the details
  void onDetail() {
    throw 'DialogAction: Not Implement onDetail()';
  }

  ///I know the
  void onKnow() {
    throw 'DialogAction: Not Implement onKnow()';
  }

  ///submit
  void onSubmit() {
    throw 'DialogAction: Not Implement onSubmit()'; }}Copy the code
  • Implementation logic class
class OneStrategy extends DialogAction {
  @override
  void onConfirm() {
    print("Sure");
  }

  @override
  void onCancel() {
    print("Cancel"); }}class TwoStrategy extends DialogAction{
  @override
  void onAgree() {
    print("Agree");
  }
  
  @override
  void onRefuse() {
    print("Reject"); }}/ /... Omit other implementations
Copy the code
  • use
void main() {
  // Based on the interface
  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;
  }

  // Aggregate popup button trigger event (different popup ok buttons can be aggregated into an onConfirm event, the same as other events)
  BusinessDialog(
    // Displays a popover of the type passed in
    type: type,
    // Confirm button
    onConfirm: () {
      strategy.onConfirm();
    },
    // Cancel button
    onCancel: () {
      strategy.onCancel();
    },
    // Agree button
    onAgree: () {
      strategy.onAgree();
    },
    // Reject button
    onRefuse: () {
      strategy.onRefuse();
    },
    // View details button
    onDetail: () {
      strategy.onDetail();
    },
    // I know the button
    onKnow: () {
      strategy.onKnow();
    },
    // Submit buttononSubmit: () { strategy.onSubmit(); }); }Copy the code
  • graphic

The evolution of a complex business scenario

Let’s see, how can a simple submission flow turn ugly

I will gradually come up with a suitable solution, and if you have any better ideas, be sure to let me know in the comments section

Business Description: Our car broke down due to irresistible reasons, we have to go to the repair shop to repair the car, the staff began to register the damaged car…

Business evolution

The first draft

Initial business

Registering a vehicle repair process is actually quite cumbersome

  • To register a new car, you need to register the vehicle details clearly: license plate, frame, vehicle model, vehicle type, entry and exit time, fuel, mileage…
  • You also need to register the user information: name, phone number, company affiliation…
  • Registered damage level: roof, bottom, steering wheel, glass, clutch, brake…
  • Car contents: seat holster, tools…
  • And other things I didn’t think of…
  • Finally: submit all the registered information

In the first draft, the business process is very clear and complex in detail, but it is not difficult to do

Second draft (actually multi-draft aggregation) : Add the following processes

External registration: Some information of a maintenance vehicle (background, wechat mini program, H5, etc.) is registered externally, and the information needs to be improved on the APP. The submission interface is different (license plate number is required).

Quick car wash: car wash business is very common, fast generation of corresponding information, submission interface is different

Booking order registration: booking a good part of the vehicle some information, can quickly register, submit the interface is different (must bring license plate number)

Because the process of vehicle registration and maintenance and the process of vehicle registration information are extremely detailed and tedious, we decided to reuse the module of new car registration

  • Because most of the logic here involves beginning and end, and the operation of the intermediate registration vehicle information is almost unchanged, the idea of reuse is feasible
  • If a vehicle registration entry is added, the new three processes must also submit this information; Therefore, reuse is imperative

The business has become more complex because of this draft requirement

The third draft

Now we’re going to do different things for different vehicle types; Car type: individual car, group car

Different types of registrations need to verify different information at the time of submission. If the verification fails, the user needs to be prompted and the submission process cannot be performed

After the submission, you need to process general services, and then jump to a page

The third draft does not have much description, but adds greatly to the complexity

  • In particular, different types of verification process is different, but also can interrupt the subsequent submission process
  • After submitting the process, you also need to jump to the common page

Developed to investigate

The first draft

  • The business process

  • The development of

Normal process development…

A second draft

  • The business process

  • thinking

For the second draft business, you can take it into consideration, how to design?

Separate judgments should be written at the beginning and end to handle the business of different processes. At least two large judgment modules should be written, and judgments may also be written in the entry module that receives data

This is a good fit for strategic mode

At the beginning, select the corresponding policy object according to the process executed, and then replace the abstract policy method with the logical block. The general process is as follows

The third draft

The business process

To discuss

  • The requirements for the third draft are, in fact, more complicated

    • The whole process is mixed with different business process processing, and different process logic has blocking downstream mechanism (green module)
    • The downstream logic then merges (ends) multiple transformations
  • Demand in this draft

    • Using the policy pattern is definitely ok
    • Block the block (green block) needs to be treated separately:The abstract method should have a return value that the outer layer can use to determine whether to proceed with the subsequent process
    • But!!! This!!!! How inelegant!
  • Consider some of the features of the business

    • Interception downstream mechanism
    • Upstream to downstream, clear direction
    • New business processes can be inserted at any time…

You can use chain of responsibility! However, some minor changes are needed! Here, we can isolate all the frequently changing modules in the chain of responsibility mode

  • Take a look at the flow chart after using the chain of responsibility model

A look at the above flow chart shows that businesses that are extremely fragmented can be designed with relatively parallel structures

  • The above process can be further analyzed and simplified: for the overall business analysis, we need to focus on the parts that change or do not change

    • Constant: The little change in the overall business is the registration information flow (the body logic), where the relevant changes are small and common to all processes
    • Change: it can be found that the beginning and end are the parts that change more frequently. We can abstract the whole logic of this place
  • Abstract changeable beginning and end

  • So we abstract the interceptor class and can make some adjustments
abstract class InterceptChainTwice<T> {
  InterceptChainTwice? next;

  voidonInit(T data) { next? .onInit(data); }void onSubmit(T data) {
    next?.onSubmit(data);
  }
}
Copy the code

Let’s look at a brief implementation of the code. The code is not important, but the implementation process and ideas

  • Abstract interceptor
abstract class InterceptChainTwice<T> {
  InterceptChainTwice? next;

  voidonInit(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;
    }

    varnode = _interceptFirst! ;while (true) {
      if (node.next == null) {
        node.next = interceptChain;
        break;
      }
      node = node.next!;
    }
  }

  voidonInit(T data) { _interceptFirst? .onInit(data); }void onSubmit(T data) {
    _interceptFirst?.onSubmit(data);
  }
}
Copy the code
  • Implementation interceptor
/// Start general purpose interceptor
class CommonIntercept extends InterceptChainTwice<String> {
  @override
  void onInit(String data) {
    // If there is a license plate, request the interface to obtain the data
    / /...
    // Populate the page
    super.onInit(data); }}/// Register new car interceptor
class RegisterNewIntercept extends InterceptChainTwice<String> {
  @override
  void onInit(String data) {
    // The process starts with a separate logic for registering a new car
    super.onInit(data);
  }

  @override
  void onSubmit(String data) {
    var isPass = false;
    // If the verification fails, intercept the downstream logic
    if(! isPass) {return;
    }
    / /...
    super.onSubmit(data); }}/// Omit other implementations
Copy the code
  • use
void main() {
  var type = 0;
  var intercepts = InterceptChainTwiceHandler();

  intercepts.add(CommonIntercept());
  intercepts.add(CarTypeDealIntercept());
  if (type == 0) {
    // Register a new car
    intercepts.add(RegisterNewCarIntercept());
  } else if (type == 1) {
    // External registration
    intercepts.add(OutRegisterIntercept());
  } else if (type == 2) {
    // Quick car wash
    intercepts.add(FastWashIntercept());
  } else {
    // Order booking
    intercepts.add(OrderRegisterIntercept());
  }
  intercepts.add(TailIntercept());

  // Business starts
  intercepts.onInit("Incoming data source");

  // Start processing N multiple logic
  / /...
  // Experience N multiple logic

  // The submit button triggers the event
  SubmitBtn(
    // Submit button
    onSubmit: () {
      intercepts.onSubmit("Incoming commit data"); }); }Copy the code

conclusion

About the code part, the key code, I write out, take a look, can certainly understand what I write

Also don’t ask me for the complete code, these business demo code after writing, delete

This column of business, in fact, is a very common business, a submission process coupled with many other processes, the whole business will slowly become ghost, full of all kinds of judgment, it is easy to let people get stuck in the mud, perhaps, at this time, you can think about the existing business, how to carry out reasonable optimization

The evolution process of this business, and the development and transformation is my thought process, if you have a better idea, please kindly comment.

Universal interceptor

I combined the idea of OkHttp and Dio API to encapsulate two general interceptors. Here is the code. If there is any deficiency, please inform me in time

Note: This is the Dart version

Abstract single method

///A layer of generic interceptors, T types must be consistent
abstract class InterceptSingle<T> {
  void intercept(T data, SingleHandler handler) => handler.next(data);
}

///Add interceptor and trigger interceptor method entry
class InterceptSingleHandler<T> {
  _InterceptSingleHandler _handler = _InterceptSingleHandler(
    index: 0,
    intercepts: [],
  );

  void add(InterceptSingle intercept) {
    // A type of interceptor can only be added once
    for (var item in _handler.intercepts) {
      if (item.runtimeType == intercept.runtimeType) {
        return;
      }
    }

    _handler.intercepts.add(intercept);
  }

  void delete(InterceptSingle intercept) {
    _handler.intercepts.remove(intercept);
  }

  voidintercept(T data) { _handler.next(data); }}///-- -- -- -- -- -- -- -- -- -- -- -- to achieve different processor reference dio API design and OkHttp ideas -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
abstract class SingleHandler {
  next(dynamic data);
}

///Implementing the Init handler
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); }}Copy the code

Abstract method of two sides

///Both layers of generic interceptors must have the same type T
abstract class InterceptTwice<T> {
  void onInit(T data, TwiceHandler handler) => handler.next(data);

  void onSubmit(T data, TwiceHandler handler) => handler.next(data);
}

///Add interceptor and trigger interceptor method entry
class InterceptTwiceHandler<T> {
  _TwiceInitHandler _init = _TwiceInitHandler(index: 0, intercepts: []);
  _TwiceSubmitHandler _submit = _TwiceSubmitHandler(index: 0, intercepts: []);

  void add(InterceptTwice intercept) {
    // A type of interceptor can only be added once
    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);
  }

  voidonSubmit(T data) { _submit.next(data); }}///-- -- -- -- -- -- -- -- -- -- -- -- to achieve different processor reference dio API design and OkHttp ideas -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
abstract class TwiceHandler {
  next(dynamic data);
}

///Implementing the Init handler
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); }}///Implementing the Submit processor
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); }}Copy the code

The last

For the first time, write an article about this combination of business

If there is a harvest, please also point a like, let me feel, you read whether there is ~~

Thanks for reading. See you next time