Replace one function with an if for every payment check by a chain of handlers: each decides or passes on, a new check is a new class, and a request that nobody answers is never approved.
A payments app checks every payment in turn: the amount against a limit, a fraud rule, then the bank. The code in the editor is one function, pay, with an if for each check and a trusted flag that skips the fraud rule. It works, but a new check means editing the middle of it. Refactor it into the Chain of Responsibility pattern: a handler per check, linked in a line, without changing a single answer.
The rules (keep them exactly):
| Check | Stops the payment when | Answer |
|---|---|---|
| Limit | the amount is over 20000 | declined, over the limit of 20000 |
| Fraud | the account is under 7 days old and the amount is over 5000 | held, new account, over 5000 |
| Bank | always answers: the user's balance covers the amount | approved, charged; otherwise declined, insufficient funds |
Build this API (Request, Verdict and a fake Bank are given):
Handler, a base class with then(next) (links the next handler and returns it, so a.then(b).then(c) works), handle(req) and an abstract step check(req). check returns a Verdict to stop the request, or None to pass it on.handle(req) appends the handler's class name to req.trail, calls check, returns the verdict if there is one, passes the request to the next handler if there is one, and otherwise returns Verdict("unhandled", "nobody handled it"). Written once, in the base class.LimitCheck(max_amount), FraudCheck(new_days, big) (which also counts how often its check ran in calls) and BankCharge(bank). Each writes only check.JavaScript and TypeScript use camelCase (ageDays), check returns null to pass on, and Verdict has status and reason.
The tests check all six answers of the old function (a trusted customer is a chain without the fraud check), which handlers looked at each request, that a handler's stop ends the chain, a new check written inside a test and linked in, a chain with no answer at the end, and that the trusted flag is gone.
Write your code, then run 9 example tests and 3 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.