Skip to content

Instantly share code, notes, and snippets.

@tuucan
Last active December 19, 2019 11:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuucan/eb44b799a22f3db73cf59a6ab5a48415 to your computer and use it in GitHub Desktop.
Save tuucan/eb44b799a22f3db73cf59a6ab5a48415 to your computer and use it in GitHub Desktop.
Typescript implementation of chain of responsibility pattern
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