Last active
March 18, 2018 13:24
-
-
Save adhrinae/cbc09144a236270e5bd5c1d076aa9d63 to your computer and use it in GitHub Desktop.
Mastering Javascript Design Pattern Chapter 05
This file contains 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 ICommand { | |
execute(): void; | |
} | |
class ConcreteCommander1 implements ICommand { | |
constructor(private receiver: Receiver) {} | |
execute(): void { | |
console.log("`execute` method of ConcreteCommand1 is being called!"); | |
this.receiver.action(); | |
} | |
} | |
class ConcreteCommander2 implements ICommand { | |
constructor(private receiver: Receiver) {} | |
execute(): void { | |
console.log("`execute` method of ConcreteCommand2 is being called!"); | |
this.receiver.action(); | |
} | |
} | |
class Invoker { | |
private commands: ICommand[] = []; | |
storeAndExecute(cmd: ICommand) { | |
this.commands.push(cmd); | |
cmd.execute(); | |
} | |
} | |
class Receiver { | |
action(): void { | |
console.log("action is being called!"); | |
} | |
} | |
const receiver: Receiver = new Receiver(), | |
command1: ICommand = new ConcreteCommander1(receiver), | |
command2: ICommand = new ConcreteCommander2(receiver), | |
invoker: Invoker = new Invoker(); | |
invoker.storeAndExecute(command1); | |
invoker.storeAndExecute(command2); |
This file contains 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 ITempHandler { | |
canHandle(msg: ITemperature): boolean; | |
handle(msg: ITemperature): ITempMessage; | |
} | |
interface ITemperature { | |
city: string; | |
temp: number; | |
} | |
interface ITempMessage { | |
temp: number; | |
message: string; | |
} | |
class TempMediator { | |
private handlers: ITempHandler[] = []; | |
addHandler(handler: ITempHandler) { | |
this.handlers.push(handler); | |
} | |
request(message: ITemperature) { | |
return this.handlers | |
.filter(handler => handler.canHandle(message)) | |
.map(handler => handler.handle(message)); | |
} | |
} | |
const tooColdHandler: ITempHandler = { | |
canHandle(message) { | |
return message.temp < 10; | |
}, | |
handle(message) { | |
return { | |
temp: message.temp, | |
message: `In ${message.city}, It is too cold!` | |
}; | |
} | |
}; | |
const tooHotHandler: ITempHandler = { | |
canHandle(message) { | |
return 30 <= message.temp; | |
}, | |
handle(message) { | |
return { | |
temp: message.temp, | |
message: `In ${message.city}, It is too hot!` | |
}; | |
} | |
}; | |
const niceDayHandler: ITempHandler = { | |
canHandle(message) { | |
return 15 <= message.temp && message.temp < 25; | |
}, | |
handle(message) { | |
return { | |
temp: message.temp, | |
message: `In ${message.city}, It should be a pleasant day today!` | |
}; | |
} | |
}; | |
const mediator = new TempMediator(); | |
mediator.addHandler(tooColdHandler); | |
mediator.addHandler(tooHotHandler); | |
mediator.addHandler(niceDayHandler); | |
console.log(mediator.request({ city: 'Seoul', temp: 9 })); | |
console.log(mediator.request({ city: 'Busan', temp: 20 })); | |
console.log(mediator.request({ city: 'Daegu', temp: 35 })); |
This file contains 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 WorldState { | |
numberOfKings: number; | |
currentKingInKingsLanding: string; | |
season: string; | |
} | |
class WorldStateProvider { | |
private numberOfKings: number; | |
private currentKingInKingsLanding: string; | |
private season: string; | |
setState(newState: { | |
numberOfKings?: number; | |
currentKingInKingsLanding?: string; | |
season?: string; | |
}) { | |
if (Object.keys(newState).length <= 0) { | |
throw new Error('There is no information to set the state'); | |
} | |
for (const prop in newState) { | |
this[prop] = newState[prop]; | |
} | |
} | |
saveMemento(): WorldState { | |
return { | |
numberOfKings: this.numberOfKings, | |
currentKingInKingsLanding: this.currentKingInKingsLanding, | |
season: this.season | |
}; | |
} | |
restoreMemento(memento: WorldState) { | |
this.setState(memento); | |
} | |
} | |
class SoothSayer { | |
private startingPoints: WorldState[] = []; | |
private currentState: WorldStateProvider = new WorldStateProvider(); | |
setInitialConditions(state: WorldState) { | |
this.currentState.setState(state); | |
} | |
storeMemento() { | |
const currentMemento = this.currentState.saveMemento(); | |
this.startingPoints.push(currentMemento); | |
} | |
alterNumberOfKingsAndForetell(numberOfKings: number) { | |
this.storeMemento(); | |
this.currentState.setState({ numberOfKings }); | |
// run some sort of prediction | |
} | |
alterSeasonAndForetell(season: string) { | |
this.storeMemento(); | |
this.currentState.setState({ season }); | |
} | |
alterCurrentKingInKingsLandingAndForeTell(currentKingInKingsLanding: string) { | |
this.storeMemento(); | |
this.currentState.setState({ currentKingInKingsLanding }); | |
} | |
tryADifferentChange() { | |
const previousState = this.startingPoints.pop(); | |
this.currentState.restoreMemento(previousState); | |
} | |
} |
This file contains 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 Player { | |
id: number; | |
name: string; | |
onKingPainKillerChange(painKillers: number): void; | |
} | |
class Spy { | |
private painKillers: number; | |
private partiesToNotify: Player[] = []; | |
subscribe(subscriber: Player) { | |
this.partiesToNotify.push(subscriber); | |
} | |
unsubscribe(subscriber: Player) { | |
this.partiesToNotify = | |
this.partiesToNotify.filter(player => player.id !== subscriber.id); | |
console.log('Subscribers has changed'); | |
console.log('Current Subscribers: ' + | |
this.partiesToNotify.map(p => p.name).join(', ')); | |
} | |
setPainKillers(painKillers: number) { | |
this.painKillers = painKillers; | |
for (const player of this.partiesToNotify) { | |
player.onKingPainKillerChange(painKillers); | |
} | |
} | |
} | |
function createPlayer(id: number, name: string): Player { | |
return { | |
id, | |
name, | |
onKingPainKillerChange(painKillers) { | |
console.log(`Player ${name} - We need ${painKillers} more painKillers!`); | |
} | |
} | |
} | |
const spy = new Spy(); | |
const p1 = createPlayer(1, 'John'); | |
const p2 = createPlayer(2, 'Susan'); | |
const p3 = createPlayer(3, 'Pheobe'); | |
spy.subscribe(p1); | |
spy.subscribe(p2); | |
spy.subscribe(p3); | |
spy.setPainKillers(12); | |
spy.unsubscribe(p2); |
This file contains 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 Actionable<T> { | |
cancel(): T; | |
verify(): T; | |
ship(): T; | |
} | |
interface State extends Actionable<State> {} | |
class Order implements Actionable<Order> { | |
private state: State; | |
constructor(state: State = new Placed()) { | |
this.state = state; | |
} | |
cancel(): Order { | |
return new Order(this.state.cancel()); | |
} | |
verify(): Order { | |
return new Order(this.state.verify()); | |
} | |
ship(): Order { | |
return new Order(this.state.ship()); | |
} | |
} | |
class Placed implements State { | |
cancel(): State { | |
console.log("Cancelling the order"); | |
return new Cancelled(); | |
} | |
verify(): State { | |
console.log("Verifying the payment"); | |
return new Verified(); | |
} | |
ship(): State { | |
console.log("Cannot ship. Payment verification is required"); | |
return this; | |
} | |
} | |
class Cancelled implements State { | |
cancel(): State { | |
console.log("Cannot cancel. Order has already been cancelled"); | |
return this; | |
} | |
verify(): State { | |
console.log("Cannot verify. Order has been cancelled"); | |
return this; | |
} | |
ship(): State { | |
console.log("Cannot ship. Order has been cancelled"); | |
return this; | |
} | |
} | |
class Verified implements State { | |
cancel(): State { | |
console.log("Cancelling the order"); | |
return new Cancelled(); | |
} | |
verify(): State { | |
console.log("Will not verify. Order has already been verified"); | |
return this; | |
} | |
ship(): State { | |
console.log("Shipping"); | |
return new Shipped(); | |
} | |
} | |
class Shipped implements State { | |
cancel(): State { | |
console.log("Cannot cancel. Order has already been shipped"); | |
return this; | |
} | |
verify(): State { | |
console.log("Will not verify. Order has already been shipped"); | |
return this; | |
} | |
ship(): State { | |
console.log("Will not ship. Order has already been shipped"); | |
return this; | |
} | |
} | |
let order = new Order(); | |
console.log(order); | |
order = order | |
.verify() | |
.ship() | |
.cancel(); | |
console.log(order); |
This file contains 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 ITravelMethod { | |
travel(source: string, destination: string): ITravelResult; | |
} | |
interface ITravelResult { | |
source: string; | |
destination: string; | |
durationInDays: number; | |
probabilityOfDeath: number; | |
cost: number; | |
} | |
class SeaGoingVessel implements ITravelMethod { | |
travel(source, destination) { | |
return { | |
source, | |
destination, | |
durationInDays: 15, | |
probabilityOfDeath: 0.25, | |
cost: 500 | |
}; | |
} | |
} | |
class Horse implements ITravelMethod { | |
travel(source, destination) { | |
return { | |
source, | |
destination, | |
durationInDays: 30, | |
probabilityOfDeath: 0.25, | |
cost: 50 | |
}; | |
} | |
} | |
class Walk implements ITravelMethod { | |
travel(source, destination) { | |
return { | |
source, | |
destination, | |
durationInDays: 150, | |
probabilityOfDeath: 0.55, | |
cost: 0 | |
}; | |
} | |
} | |
function getCurrentMoney(): number { | |
return Math.floor(Math.random() * 100); | |
} | |
const currentMoney = getCurrentMoney(); | |
let strat: ITravelMethod; | |
if (currentMoney > 500) { | |
strat = new SeaGoingVessel(); | |
} else if (currentMoney > 50) { | |
strat = new Horse(); | |
} else { | |
strat = new Walk(); | |
} | |
console.log(strat.travel('Seoul', 'Jeju')); |
This file contains 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
namespace Visitor { | |
interface IElementVisitor { | |
visitElement(element: Element): void; | |
visitElementNode(elementNode: ElementNode): void; | |
} | |
export class Element { | |
private _name: string; | |
private _parent: ElementNode; | |
constructor(name: string, parent?: ElementNode) { | |
if (!name) { | |
throw new Error("Argument null exception!"); | |
} | |
this._name = name; | |
this._parent = parent; | |
} | |
get name(): string { | |
return this._name; | |
} | |
get parent(): ElementNode { | |
return this._parent; | |
} | |
set parent(value: ElementNode) { | |
this._parent = value; | |
} | |
get depth(): number { | |
if (this._parent) { | |
return this._parent.depth + 1; | |
} | |
return 0; | |
} | |
accept(visitor: IElementVisitor) { | |
visitor.visitElement(this); | |
} | |
} | |
export class ElementNode extends Element { | |
private _children: Element[] = []; | |
constructor(name: string, parent?: ElementNode) { | |
super(name, parent); | |
} | |
get length(): number { | |
return this._children.length; | |
} | |
appendChild(child: Element): ElementNode { | |
child.parent = this; | |
this._children.push(child); | |
return this; | |
} | |
accept(visitor: IElementVisitor) { | |
visitor.visitElementNode(this); | |
this._children.forEach(function(child) { | |
child.accept(visitor); | |
}); | |
} | |
} | |
export class LogWriter implements IElementVisitor { | |
visitElement(element: Element) { | |
console.log("LogWriter is visiting the element: '" + element.name + "'"); | |
} | |
visitElementNode(elementNode: ElementNode) { | |
console.log( | |
"LogWrite is visiting the element node: '" + | |
elementNode.name + | |
"'. Which has: " + | |
elementNode.length + | |
" child nodes." | |
); | |
} | |
} | |
export class ConsoleWriter implements IElementVisitor { | |
visitElement(element: Element) { | |
console.log( | |
"ConsoleWriter is visiting the element: '" + element.name + "'" | |
); | |
} | |
visitElementNode(elementNode: ElementNode) { | |
console.log( | |
"ConsoleWriter is visiting the element node: '" + | |
elementNode.name + | |
"'. Which has: " + | |
elementNode.length + | |
" child nodes." | |
); | |
} | |
} | |
} | |
const constructedTree = new Visitor.ElementNode("first") | |
.appendChild(new Visitor.Element("firstChild")) | |
.appendChild( | |
new Visitor.ElementNode("secondChild").appendChild( | |
new Visitor.Element("furtherDown") | |
) | |
); | |
const logwriter = new Visitor.LogWriter(); | |
constructedTree.accept(logwriter); | |
const consolewriter = new Visitor.ConsoleWriter(); | |
constructedTree.accept(consolewriter); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment