Last active
December 19, 2019 11:12
-
-
Save tuucan/eb44b799a22f3db73cf59a6ab5a48415 to your computer and use it in GitHub Desktop.
Typescript implementation of chain of responsibility pattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Handler<T> { | |
setSuccessor: (successor: T) => void; | |
handle(...args: any): void; | |
} | |
/** | |
* Abstract base class for handlers | |
* hadnles setting and calling successors | |
* */ | |
abstract class AbstractHandler<T> implements Handler<AbstractHandler<T>> { | |
private successor: AbstractHandler<T> | undefined = undefined; | |
setSuccessor(successor: AbstractHandler<T>) { | |
this.successor = successor; | |
} | |
callSuccessor(...args: any) { | |
if(this.successor) | |
this.successor.handle(...args); | |
} | |
abstract handle(...args: any): void; | |
} | |
/** | |
* Abstract base class for chain managers | |
* handles chaining handlers passed to constructor | |
* | |
* @example | |
* new ChainManager<RouteHandler>( | |
* new RouteHandler(_), | |
* new AnotherRouteHandler(_), | |
* new YetAnotherRouteHandler(_), | |
* ); | |
* */ | |
abstract class ChainManager<T extends AbstractHandler<T>> { | |
private chain: T; | |
constructor(...handlers: T[]) { | |
this.chain = handlers.reduce(setSuccessor); | |
} | |
protected handle(...args: any) { | |
this.chain.handle(...args); | |
} | |
} | |
/** Utility functions */ | |
function setSuccessor<T extends AbstractHandler<T>> (chain: T, successor: T, currentIdx: number, handlers: T[]): T { | |
currentIdx && handlers[currentIdx-1].setSuccessor(successor); | |
return chain; | |
} | |
/** Basic Example */ | |
/** Handlers */ | |
// we can either use this interface as our common type | |
// exm: ChainManager<WithdrawHandler> | |
// or if we want to have additional common methods that handlers can use | |
// we can create an abstract class that implements WithdrawHandler | |
interface WithdrawHandler extends AbstractHandler<WithdrawHandler> { } | |
abstract class BaseWithdrawHandler extends AbstractHandler<WithdrawHandler> implements WithdrawHandler { | |
protected billSize: number; | |
public constructor(billSize: number) { | |
super(); | |
this.billSize = billSize; | |
} | |
protected ejectMoney(numOfBills: number) { | |
console.log(`Ejecting ${numOfBills} ${this.billSize} TL bill(s).`); | |
} | |
} | |
// we can create seperate handlers | |
// in this case we can create handlers from a base class | |
class MoneyStack extends BaseWithdrawHandler { | |
public handle(withdrawAmount: number) { | |
const numberOfBills = Math.floor(withdrawAmount / this.billSize); | |
if (numberOfBills) { | |
this.ejectMoney(numberOfBills); | |
withdrawAmount -= (this.billSize * numberOfBills); | |
} | |
withdrawAmount && this.callSuccessor(withdrawAmount); | |
} | |
} | |
class ATM extends ChainManager<WithdrawHandler> { | |
public withdraw(amount: number) { | |
this.handle(amount); | |
} | |
} | |
/** Usage */ | |
const atm = new ATM( | |
new MoneyStack(200), | |
new MoneyStack(100), | |
new MoneyStack(50), | |
new MoneyStack(20), | |
new MoneyStack(10), | |
new MoneyStack(5), | |
); | |
atm.withdraw(595); | |
/** Output | |
* Ejecting 2 200 TL bill(s). | |
* Ejecting 1 100 TL bill(s). | |
* Ejecting 1 50 TL bill(s). | |
* Ejecting 2 20 TL bill(s). | |
* Ejecting 1 5 TL bill(s). | |
* */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment