Skip to content

Instantly share code, notes, and snippets.

@adhrinae
Last active March 18, 2018 13:24
Show Gist options
  • Save adhrinae/cbc09144a236270e5bd5c1d076aa9d63 to your computer and use it in GitHub Desktop.
Save adhrinae/cbc09144a236270e5bd5c1d076aa9d63 to your computer and use it in GitHub Desktop.
Mastering Javascript Design Pattern Chapter 05
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);
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 }));
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);
}
}
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);
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);
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'));
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