Skip to content

Instantly share code, notes, and snippets.

@adhrinae
Last active March 12, 2018 05:18
Show Gist options
  • Save adhrinae/39c53a112f6f6ab21c0dd56c107da384 to your computer and use it in GitHub Desktop.
Save adhrinae/39c53a112f6f6ab21c0dd56c107da384 to your computer and use it in GitHub Desktop.
Mastering Javascript Design Pattern Chapter 04
interface Ship {
SetRudderAngleTo(angle: number);
SetSailConfiguration(configuration: SailConfiguration);
SetSailAngle(SailId: number, sailAngle: number);
GetCurrentBearing(): number;
GetCurrentSpeedEstimate(): number;
ShiftCrewWeightTo(weightToShift: number, locationId: number);
}
interface SailConfiguration {
crews: number;
foods: number;
}
interface SimpleShip {
TurnLeft();
TurnRight();
GoFoward();
}
// 예제 코드의 구현방법이 잘못되었다고 느끼는 부분이 있는데,
// Apater에 사용할 Ship을 주입받지 않고 외부에 글로벌 변수에서 참고한다는 점이다.
class ShipAdater implements SimpleShip {
private ship: Ship;
constructor(ship: Ship) {
this.ship = ship;
}
TurnLeft() {
this.ship.SetRudderAngleTo(-30);
this.ship.SetSailAngle(3, 12);
}
TurnRight() {
this.ship.SetRudderAngleTo(30);
this.ship.SetSailAngle(5, -9);
}
GoFoward() {
this.ship.SetRudderAngleTo(0);
this.ship.SetSailAngle(0, 0);
this.ship.GetCurrentSpeedEstimate();
}
}
// 메서드의 구현부는 생략
class Boat implements Ship {
SetRudderAngleTo(angle: number) {
throw new Error("Method not implemented.");
}
SetSailConfiguration(configuration: SailConfiguration) {
throw new Error("Method not implemented.");
}
SetSailAngle(SailId: number, sailAngle: number) {
throw new Error("Method not implemented.");
}
GetCurrentBearing(): number {
throw new Error("Method not implemented.");
}
GetCurrentSpeedEstimate(): number {
throw new Error("Method not implemented.");
}
ShiftCrewWeightTo(weightToShift: number, locationId: number) {
throw new Error("Method not implemented.");
}
}
const ship = new ShipAdater(new Boat);
ship.GoFoward();
ship.TurnLeft();
namespace Religion {
export interface God {
prayTo(withSomething?: Sacrifice | HumanSacrifice | PrayerPurpose): void;
}
export class OldGods implements God {
prayTo(sacrifice: Sacrifice) {
console.log('We Old Gods hear your prayer');
}
}
export class DrownedGod {
prayTo(humanSacrifice: HumanSacrifice) {
console.log('*BUBBLE* GURGLE');
}
}
export class SevenGods {
prayTo(prayerPurpose: PrayerPurpose) {
console.log(
'Sorry there are a lot of us, it gets confusing here. Did you pray for something?'
);
}
}
class Sacrifice {}
class HumanSacrifice {}
class PrayerPurpose {}
export class PrayerPurposeProvider {
GetPurpose() {
return new PrayerPurpose();
}
}
export class OldGodsAdapter {
constructor(private oldGoods: OldGods) {}
prayTo() {
const sacrifice = new Sacrifice();
this.oldGoods.prayTo(sacrifice);
}
}
export class DrownedGodAdapter {
constructor(private drownedGod: DrownedGod) {}
prayTo() {
const sacrifice = new HumanSacrifice();
this.drownedGod.prayTo(sacrifice);
}
}
export class SevenGodsAdapter {
constructor(
private sevenGods: SevenGods,
private prayerPurposeProvider: PrayerPurposeProvider
) {}
prayTo() {
this.sevenGods.prayTo(this.prayerPurposeProvider.GetPurpose());
}
}
}
const god1 = new Religion.SevenGodsAdapter(
new Religion.SevenGods(),
new Religion.PrayerPurposeProvider()
);
const god2 = new Religion.DrownedGodAdapter(new Religion.DrownedGod());
const god3 = new Religion.OldGodsAdapter(new Religion.OldGods());
const gods: Religion.God[] = [god1, god2, god3];
for (const god of gods) {
god.prayTo();
}
namespace Composite {
interface IMenuComponent {
render(parentElement: HTMLElement): void;
}
interface IMenuItem extends IMenuComponent {}
interface IMenu extends IMenuComponent {
children: IMenuComponent[];
}
class MenuItemLink implements IMenuItem {
constructor(public displayName: string, public url: string) {}
render(parentElement: HTMLElement) {
const link: HTMLAnchorElement = document.createElement('a');
link.textContent = this.displayName;
link.href = this.url;
parentElement.appendChild(link);
}
}
class MenuItemImageLink implements IMenuItem {
constructor(
public displayName: string,
public url: string,
public imageUrl: string
) {}
render(parentElement: HTMLElement) {
const link: HTMLAnchorElement = document.createElement('a');
link.href = this.url;
const img: HTMLImageElement = document.createElement('img');
img.src = this.imageUrl;
link.appendChild(img);
const text = document.createTextNode(this.displayName);
link.appendChild(text);
parentElement.appendChild(link);
}
}
class Menu implements IMenu {
public children: IMenuComponent[] = [];
constructor(public displayName?: string) {}
render(parentElement: HTMLElement) {
if (this.displayName) {
parentElement.appendChild(document.createTextNode(this.displayName));
}
const ul: HTMLUListElement = document.createElement('ul');
this.children.forEach(child => {
const li: HTMLLIElement = document.createElement('li');
child.render(li);
ul.appendChild(li);
});
parentElement.appendChild(ul);
}
}
window.addEventListener('load', function() {
const menu: IMenu = new Menu();
for (var i = 1; i <= 3; i++) {
menu.children.push(new MenuItemLink('Link ' + i, '?id=' + i));
}
menu.children.push(
new MenuItemImageLink(
'Contact',
'mailto:info@sample.com',
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAITSURBVBgZpcHLThNhGIDh9/vn7/RApwc5VCmFWBPi1mvwAlx7BW69Afeu3bozcSE7E02ILjCRhRrds8AEbKVS2gIdSjvTmf+TYqLu+zyiqszDMCf75PnnnVwhuNcLpwsXk8Q4BYeSOsWpkqrinJI6JXVK6lSRdDq9PO+19vb37XK13Hj0YLMUTVVyWY//Cf8IVwQEGEeJN47S1YdPo4npDpNmnDh5udOh1YsZRcph39EaONpnjs65oxsqvZEyTaHdj3n2psPpKDLBcuOOGUWpZDOG+q0S7751ObuYUisJGQ98T/Ct4Fuo5IX+MGZr95jKjRKLlSxXxFxOEmaaN4us1Upsf+1yGk5ZKhp8C74H5ZwwCGO2drssLZZo1ouIcs2MJikz1oPmapHlaoFXH1oMwphyTghyQj+MefG+RblcoLlaJG/5y4zGCTMikEwTctaxXq/w9kuXdm9Cuzfh9acujXqFwE8xmuBb/hCwl1GKAnGccDwIadQCfD9DZ5Dj494QA2w2qtQW84wmMZ1eyFI1QBVQwV5GiaZOpdsPaSwH5HMZULi9UmB9pYAAouBQbMHHrgQcnQwZV/KgTu1o8PMgipONu2t5KeaNiEkxgAiICDMCCFeEK5aNauAOfoXx8KR9ZOOLk8P7j7er2WBhwWY9sdbDeIJnwBjBWBBAhGsCmiZxPD4/7Z98b/0QVWUehjkZ5vQb/Un5e/DIsVsAAAAASUVORK5CYII='
)
);
const subMenu: IMenu = new Menu('Sub Menu');
for (var i = 1; i <= 2; i++) {
subMenu.children.push(new MenuItemLink('Sub Link ' + i, '?id=' + i));
}
menu.children.push(subMenu);
const contentDiv = document.getElementById('output');
menu.render(contentDiv);
});
}
interface IArmor {
calculateDamageFromHit(hit: IHit);
getArmorIntegrity(): number;
}
interface IHit {
location: string;
weapon: string;
strength: number;
}
class BasicArmor implements IArmor {
constructor(private baseArmorPoint: number = 1) {}
calculateDamageFromHit(hit: IHit) {
console.log(`You got damage in ${hit.location} by ${hit.weapon}`);
return this.baseArmorPoint * hit.strength;
}
getArmorIntegrity() {
return this.baseArmorPoint;
}
}
class ChainMail implements IArmor {
constructor(private decoratedArmor: IArmor) {}
calculateDamageFromHit(hit: IHit) {
const reducedHit = {
...hit,
strength: hit.strength * 0.8,
};
return this.decoratedArmor.calculateDamageFromHit(reducedHit);
}
getArmorIntegrity() {
return 0.9 * this.decoratedArmor.getArmorIntegrity();
}
}
const armor = new ChainMail(new BasicArmor());
console.log(
armor.calculateDamageFromHit({
location: 'head',
weapon: 'Long Sword',
strength: 12,
})
);
class BlurayPlayer {
on() {
console.log('Bluray player turning on...');
}
turnOff() {
console.log('Bluray player turning off...');
}
play() {
console.log('Playing bluray disc...');
}
}
class Amplifier {
on() {
console.log('Amplifier is turning on...');
}
turnOff() {
console.log('Amplifier turning off...');
}
setSource(source: string) {
console.log(`Setting source to ${source}`);
}
setVolume(volumeLevel: number) {
console.log(`Setting volume to ${volumeLevel}`);
}
}
class Lights {
dim() {
console.log('Lights are dimming...');
}
}
class TV {
turnOn() {
console.log('TV is turning on...');
}
turnOff() {
console.log('TV is turning off...');
}
}
class PopcornMaker {
turnOn() {
console.log('Popcorn maker is turning on...');
}
turnOff() {
console.log('Popcorn maker is turning off...');
}
pop() {
console.log('Popping corn!');
}
}
class HomeTheaterFacade {
private amp: Amplifier;
private bluray: BlurayPlayer;
private lights: Lights;
private tv: TV;
private popcornMaker: PopcornMaker;
constructor({
amp,
bluray,
lights,
tv,
popcornMaker,
}: {
amp: Amplifier;
bluray: BlurayPlayer;
lights: Lights;
tv: TV;
popcornMaker: PopcornMaker;
}) {
this.amp = amp;
this.bluray = bluray;
this.lights = lights;
this.tv = tv;
this.popcornMaker = popcornMaker;
}
watchMovie() {
this.popcornMaker.turnOn();
this.popcornMaker.pop();
this.lights.dim();
this.tv.turnOn();
this.amp.on();
this.amp.setSource('bluray');
this.amp.setVolume(11);
this.bluray.on();
this.bluray.play();
}
endMovie() {
this.popcornMaker.turnOff();
this.amp.turnOff();
this.tv.turnOff();
this.bluray.turnOff();
}
}
// ========================================================
const homeTheater = new HomeTheaterFacade({
amp: new Amplifier(),
bluray: new BlurayPlayer(),
lights: new Lights(),
tv: new TV(),
popcornMaker: new PopcornMaker(),
});
homeTheater.watchMovie();
interface IResource {
fetch(): void;
}
class ResourceProxy implements IResource {
constructor(private resource: Resource) {}
fetch() {
console.log('invoke resource fetch method');
this.resource.fetch();
}
}
class Resource implements IResource {
fetch() {
console.log('fetching resource');
}
}
const proxy = new ResourceProxy(new Resource());
proxy.fetch();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment