Typescript 2.0 Design Patterns

Sunday, November 20, 2016

Simple modern Typescript Design Patterns

Open Source Rules!

Creational

abstract factory: create an instance of several families of classes


interface IAbstractTechnologyA {
    functionA(): string;
}
interface IAbstractTechnologyB {
    functionB(): number;
}
interface IAbstractFactory {
    createTechnologyA(): IAbstractTechnologyA;
    createTechnologyB(): IAbstractTechnologyB;
}
class TechnologyA1 implements IAbstractTechnologyA {
    functionA = () => { return "this is functiona of technologya1";}
}
class TechnologyB1 implements IAbstractTechnologyB {
    functionB = () => { return 100;}
}
class TechnologyA2 implements IAbstractTechnologyA {
    functionA = () => { return "this is functiona of technologya2";}
}
class TechnologyB2 implements IAbstractTechnologyB {
    functionB = () => { return 101;}
}
class ConcreteFactory1 implements IAbstractFactory {
    createTechnologyA(): IAbstractTechnologyA {
            return new TechnologyA1();
    }
    createTechnologyB(): IAbstractTechnologyB {
            return new TechnologyB1();
    }
}
class ConcreteFactory2 implements IAbstractFactory {
    createTechnologyA(): IAbstractTechnologyA {
        return new TechnologyA2();
    }
    createTechnologyB(): IAbstractTechnologyB {
        return new TechnologyB2();
    }
}
class AbstractFactory {
        private abstractTechnologyA: IAbstractTechnologyA;
        private abstractTechnologyB: IAbstractTechnologyB;
        constructor(factory: IAbstractFactory){
            this.abstractTechnologyA = factory.createTechnologyA();
            this.abstractTechnologyB = factory.createTechnologyB();
        }
        public execute(): string {
            return this.abstractTechnologyA.functionA() + " " + this.abstractTechnologyB.functionB();
        }
}
function show() {
        let factory1: IAbstractFactory = new ConcreteFactory1();
        let abstracter1: AbstractFactory = new AbstractFactory(factory1);
        console.log(abstracter1.execute());
        let factory2: IAbstractFactory = new ConcreteFactory2();
        let abstracter2: AbstractFactory = new AbstractFactory(factory2);
        console.log(abstracter2.execute());
}
show();                        
                    

builder: separates object construction from its representation


    class TechnologyBuilder {
    private name: string;
    private power: number;
    private memory: string;
    private isKiller: boolean;
    constructor(name: string) {this.name = name;}
    get Name() { return this.name;}
    setPower(value: number): TechnologyBuilder {
        this.power = value;
        return this;
    }
    get Power() { return this.power;}
    setMemory(value: string): TechnologyBuilder {
        this.memory = value;
        return this;
    }
    get Memory() { return this.memory;}
    setIsKiller(value: boolean): TechnologyBuilder {
        this.isKiller = value;
        return this;
    }
    get IsKiller() { return this.isKiller;}
    build(): Technology { return new Technology(this);}
}
class Technology {
    private name: string;
    private power: number;
    private memory: string;
    private isKiller: boolean;
    constructor(builder: TechnologyBuilder){
        this.name = builder.Name;
        this.power = builder.Power;
        this.memory = builder.Memory;
        this.isKiller = builder.IsKiller;
    }
    get Name() { return this.name;}
    get Power() { return this.power;}
    get Memory() { return this.memory;}
    get IsKiller() { return this.isKiller;}
}
class Builder {
        execute(): string {
            const tech: Technology = new TechnologyBuilder("V10")
                                            .setPower(100)
                                            .setMemory("10101011")
                                            .setIsKiller(true)
                                            .build();
        return `${tech.Name} ${tech.Power} ${tech.Memory} ${tech.IsKiller}`;
        }
}
let builder = new Builder();
console.log(builder.execute());                            
                       
                    

factory method: creates an instance of several derived classes

                    
interface IAbstractTechnology {
    function(param?: any): string;
}
class ConcreteTechnologyA implements IAbstractTechnology {
    function = (param?: any) => { return "function of concretetechnologya";}
}
class ConcreteTechnologyB implements IAbstractTechnology {
    function = (param?: any) => { return "function of concretetechnologyb";}
}
class ConcreteTechnologyC implements IAbstractTechnology {
    function = (param?: any) => { return "function of concretetechnologyc";}
}
class UnknownTechnology implements IAbstractTechnology {
    function = (param?: any) => { return "unknown function/technology";}
}
class TechnologyFactory {
    public static createTechnology(type: string): IAbstractTechnology{
            return type === "A" ? new ConcreteTechnologyA()
            : type === "B" ? new ConcreteTechnologyB() : type === "C" ? new ConcreteTechnologyC() :
            new UnknownTechnology();
    }
}
class FactoryMethod {
    show(param: string): string {
        const a: IAbstractTechnology = TechnologyFactory.createTechnology(param);
        return a.function();
    }
}
let res: FactoryMethod = new FactoryMethod();
console.log(res.show("A"));
console.log(res.show("B"));
                        
                    

prototype: a fully initialized instance to be copied or cloned

                    
interface IPrototype {
    copy(): IPrototype;
    toString(): string;
}
class Concrete0 implements IPrototype {
    copy(): IPrototype {
        return new Concrete0();
    }
    toString(): string {
        return "this is concrete0";
    }
}
class Concrete1 implements IPrototype {
    copy(): IPrototype {
        return new Concrete1();
    }
    toString(): string {
        return "this is concrete1";
    }
}
class Concrete2 implements IPrototype {
    copy(): IPrototype {
        return new Concrete2();
    }
    toString(): string {
        return "this is concrete2";
    }
}
class Concrete3 implements IPrototype {
    copy(): IPrototype {
        return new Concrete3();
    }
    toString(): string {
        return "this is concrete3";
    }
}
class Prototype {
    private prototypeMap: {[supersex: string] : IPrototype} = {};
    constructor() {
        this.prototypeMap["c0"] = new Concrete0();
        this.prototypeMap["c1"] = new Concrete1();
        this.prototypeMap["c2"] = new Concrete2();
        this.prototypeMap["c3"] = new Concrete3();
    }
    createOne(she: string): IPrototype {
        console.log(she);
        return this.prototypeMap[she].copy();
    }
}
let builder: Prototype = new Prototype();
for (let i = 0; i <= 3; i += 1){
    console.log(builder.createOne("c" + i).toString());
}
                        
                    

singleton: a class of which only a single instance can exist

                    
class Singleton {
    private static instance: Singleton;
    constructor(){}
    static get Instance(){
        if (this.instance === null || this.instance === undefined){
            this.instance = new Singleton();
        }
        return this.instance;
    }
    show(): string {
        let singleton0 = Singleton.Instance;
        let singleton1 = Singleton.Instance;
        let singleton2 = Singleton.Instance;
        if (singleton0 === singleton1 && singleton1 === singleton2){
            return "three singletons are equivalent";
        } else {
            return "three singletons are not equivalent";
        }
    }
}
let s: Singleton = new Singleton();
console.log(s.show());
                        
                    

Structural

adapter: match interfaces of different classes

                    
class Adaptee {
    public method(): void {
        console.log("method of adaptee is being invoked");
    }
}
interface ITarget {
    call(): void;
}
class Adapter implements ITarget {
    public call(): void {
        console.log("adapters call method is being invoked");
        const adaptee: Adaptee = new Adaptee();
        adaptee.method();
    }
}
let adapter: Adapter = new Adapter();
adapter.call();
                        
                    

bridge: separate an object's interface from its implementation

                    
class Abstraction {
        implementor: Implementor;
        constructor(imp: Implementor){
            this.implementor = imp;
        }
        public callIt(s: string): void {
            throw new Error("this method is abstract");
        }
}
class RefinedAbstractionA extends Abstraction {
        constructor(imp: Implementor){
            super(imp);
        }
        public callIt(s: string): void {
            console.log("this is refined abstractiona");
            this.implementor.callee(s);
        }
}
class RefinedAbstractionB extends Abstraction {
        constructor(imp: Implementor){
            super(imp);
        }
        public callIt(s: string): void {
            console.log("this is refined abstractionb");
            this.implementor.callee(s);
        }
}
interface Implementor {
        callee(s: any): void;
}
class ConcreteImplementorA implements Implementor {
     public callee(s: any): void {
         console.log("callee of concreteimplementora is being invoked");
         console.log(s);
     }
}
class ConcreteImplementorB implements Implementor {
    public callee(s: any): void {
        console.log("callee of concreteimplentorb is being invoked");
        console.log(s);
    }
}
let abstractiona: Abstraction = new RefinedAbstractionA(new ConcreteImplementorA());
let abstractionb: Abstraction = new RefinedAbstractionB(new ConcreteImplementorB());
abstractiona.callIt("abstractiona");
abstractionb.callIt("abstractionb");
                        
                    

composite: a tree structure of simple and composite objects

                    
interface Component {
    operation(): void;
}
class Composite implements Component {
     private list: Component[];
     private s: string;
     constructor(s: string){
         this.list = [];
         this.s = s;
     }
     public operation(): void {
         console.log(`operation of composite ${this.s} is invoked`);
         for(let i = 0; i < this.list.length; i +=1){
             this.list[i].operation();
         }
      }
      public add(c: Component){
          this.list.push(c);
      }
      public remove(i: number){
          if (this.list.length <= 1){throw new Error("index out of bounds");}
          this.list.splice(i, i === 0 ? 1 : i);
      }
}
class Leaf implements Component {
    private s: string;
    constructor(s: string){
        this.s = s;
    }
    public operation(): void {
        console.log(`operation of leaf ${this.s} is invoked`);
    }
}
let leaf1 = new Leaf("1");
let leaf2 = new Leaf("2");
let leaf3 = new Leaf("3");
let comp1 = new Composite("comp1");
let comp2 = new Composite("comp2");
comp1.add(leaf1); comp1.add(leaf2); comp1.add(leaf3); comp1.remove(2); comp1.operation();
comp2.add(leaf1); comp2.add(leaf3); comp2.operation();
                        
                    

decorator: add responsibilities to objects dynamically

                    
interface IComponent {
    operation(): void;
}
class ConcreteComponent implements IComponent {
    private s: string;
    constructor(s: string){
        this.s = s;
    }
    public operation(): void {
        console.log(`operation of concretecomponent ${this.s} is being invoked`);
    }
}
class Decorator implements IComponent {
    private component: IComponent;
    private id: number;
    constructor(id: number, component: IComponent){
        this.id = id;
        this.component = component;
    }
    public get Id(){
        return this.id;
    }
    public operation(): void {
        console.log(`operation of decorator ${this.id} is being invoked`);
        this.component.operation();
    }
}
class ConcreteDecorator extends Decorator {
        constructor(id: number, component: IComponent){
            super(id, component);
        }
        public operation(): void {
            super.operation();
            console.log(`operation of concretedecorator ${this.Id} is being invoked`);
        }
}
let decorator1: Decorator = new ConcreteDecorator(1, new ConcreteComponent("comp1"));
decorator1.operation();
                        
                    

facade: a single class that represents an entire subsystem

                    
  class Technology1 {
    public function1(): void {
        console.log("function1 of technology1");
    }
}
class Technology2 {
    public function2(): void {
        console.log("function2 of technology2");
    }
}
class Technology3 {
    public function3(): void {
        console.log("function3 of technology3");
    }
}
class Facade {
    private tech1: Technology1 = new Technology1();
    private tech2: Technology2 = new Technology2();
    private tech3: Technology3 = new Technology3();
    public implementation1(): void {
        console.log("implementation1 is invoked ---");
        this.tech1.function1();
        this.tech2.function2();
        console.log("------------------------------");
    }
    public implementation2(): void {
        console.log("implementation2 is invoked ---");
        this.tech1.function1();
        this.tech3.function3();
        console.log("------------------------------");
    }
}
let facade1: Facade = new Facade();
facade1.implementation1();
facade1.implementation2();
                        
                    

flyweight: a fine-grained instance used for efficient sharing

                    
interface IFlyweight {
    operation(s: string): void;
}
class ConcreteFlyweight implements IFlyweight {
    private intrinsicState: string;
    constructor(intrinsicState: string){
        this.intrinsicState = intrinsicState;
    }
    public operation(s: string): void {
        console.log(`operation of concreteflyweight ${s} is being invoked`);
    }
}
class UnsharedConcreteFlyweight implements IFlyweight {
    private allState: number;
    constructor(allState: number){
        this.allState = allState;
    }
    public operation(s: string): void{
        console.log(`operation of unsharedconcreteflyweight ${s} is being invoked`);
    }
}
class FlyweightFactory {
    private fliesMap: {[s: string]: IFlyweight;} = {};
    constructor() {}
    public getFlyweight(key: string): IFlyweight {
        if (this.fliesMap[key] === undefined || null){
            this.fliesMap[key] = new ConcreteFlyweight(key);
        }
        return this.fliesMap[key];
    }
}
let factory: FlyweightFactory = new FlyweightFactory();
let conc1: ConcreteFlyweight = factory.getFlyweight("conc1");
let conc2: ConcreteFlyweight = factory.getFlyweight("conc2");
conc1.operation("1");
conc2.operation("2");
                        
                    

proxy: an object representing another object

                    
interface ISubject {
    doTask(): void;
}
class Proxxy implements ISubject {
    private realSubject: RealSubject;
    private s: string;
    constructor(s: string){
        this.s = s;
    }
    public doTask(): void {
        console.log(`dotask() of proxxy(${this.s}) is being invoked`);
        if (this.realSubject === null || this.realSubject === undefined){
            console.log("creating a new real subject");
            this.realSubject = new RealSubject(this.s);
        }
        this.realSubject.doTask();
    }
}
class RealSubject implements ISubject {
    private s: string;
    constructor(s: string) {this.s = s;}
    public doTask(): void {
        console.log(`dotask() of realsubject(${this.s}) is being invoked`);
    }
}
let proxy1: Proxxy = new Proxxy("proxy1");
let proxy2: Proxxy = new Proxxy("proxy2");
proxy1.doTask();
proxy1.doTask();
proxy2.doTask();
proxy2.doTask();
proxy1.doTask();
                        
                    

Behavioral

chain of responsibility: a way of passing a request between a chain of objects

                    
 class Handler {
    private handler: Handler;
    private req: number;
    constructor(req: number){
        this.req = req;
    }
    public setHandler(handler: Handler){
        this.handler = handler;
    }
    public operation(msg: string, req: number){
        if (req <= this.req){
            this.handlerRequest(msg);
        } else if (this.handler !== null && this.handler !== undefined){
            this.handler.operation(msg, req);
        }
    }
    public handlerRequest(msg: string): void{
        throw new Error("abstract method");
    }
}
class ConcreteHandler1 extends Handler {
    constructor(req: number){
        super(req);
    }
    public handlerRequest(msg: string){
        console.log(`message(concretehandler1) :: ${msg}`);
    }
}
class ConcreteHandler2 extends Handler {
    constructor(req: number){
        super(req);
    }
    public handlerRequest(msg: string){
        console.log(`message(concreteHandler2) :: ${msg}`);
    }
}
class ConcreteHandler3 extends Handler {
    constructor(req: number){
        super(req);
    }
    public handlerRequest(msg: string){
        console.log(`message(concreteHandler3) :: ${msg}`);
    }
}
function show(): void {
    let h1: Handler = new ConcreteHandler1(2);
    let h2: Handler = new ConcreteHandler2(6);
    let h3: Handler = new ConcreteHandler3(19);
    let reqs: number[] = [3, 8, 24, 35, 5, 6, 9, 4];
    h1.setHandler(h2);
    h2.setHandler(h3);
    for (let i = 0; i < reqs.length; i += 1 ){
        h1.operation("operation is fired!", reqs[i]);
    }
}
show();
                        
                    

command: encapsulate a command request as an object

                    
class Command {
    public execute(): void {
        throw new Error("abstract method");
    }
}
class ConcreteCommand1 extends Command {
    private receiver: Receiver;
    constructor(receiver: Receiver){
        super();
        this.receiver = receiver;
    }
    public execute(): void {
        console.log("execute method of concretecommand1 is being invoked");
        this.receiver.action();
    }
}
class ConcreteCommand2 extends Command {
    private receiver: Receiver;
    constructor(receiver: Receiver){
        super();
        this.receiver = receiver;
    }
    public execute(): void {
        console.log("execute method of concretecommand2 is being invoked");
        this.receiver.action();
    }
}
class Invoker {
    private commands: Command[];
    constructor(){
        this.commands = [];
    }
    public storeAndExecute(cmd: Command){
        this.commands.push(cmd);
        cmd.execute();
    }
}
class Receiver {
    public action(): void {
        console.log("action is being called");
    }
}
let receiver: Receiver = new Receiver();
let command1: Command = new ConcreteCommand1(receiver);
let command2: Command = new ConcreteCommand2(receiver);
let invoker: Invoker = new Invoker();
invoker.storeAndExecute(command1);
invoker.storeAndExecute(command2);
                        
                    

interpreter: a way to include language elements in a program

                    
class Context {
    public micro: string = "system";
    public macro: string = "architecture";
    public static unknown: boolean = true || false;
}
interface IAbstractTechnology {
    interpret(context: Context): void;
}
class ConcreteTechnology implements IAbstractTechnology {
    public interpret(context: Context): void {
        console.log("interpret method of concretetechnology is being invoked");
    }
}
class WorkingSoftwareTechnology implements IAbstractTechnology {
    public interpret(context: Context): void {
        console.log("interpret method of workingsoftwaretechnology is being invoked");
    }
}
let context: Context = new Context();
let list = [];
let i: number = 0;
let max: number = 0;
list.push(new WorkingSoftwareTechnology());
list.push(new WorkingSoftwareTechnology());
list.push(new WorkingSoftwareTechnology());
list.push(new ConcreteTechnology());
list.push(new WorkingSoftwareTechnology());
list.push(new WorkingSoftwareTechnology());
list.push(new ConcreteTechnology());
list.push(new ConcreteTechnology());
for (max = list.length; i < max; i +=1){
    list[i].interpret(context);
}
                        
                    

iterator: sequentially access the elements of a collection

                    
interface IIterator {
    next(): any;
    hasNext(): boolean;
}
interface IAggregator {
    createIterator(): IIterator;
}
class ConcreteIterator implements IIterator {
    private collection: any[] = [];
    private position: number = 0;
    constructor(collection: any[]){
        this.collection = collection;
    }
    next(): any {
        let result = this.collection[this.position];
        this.position += 1;
        return result;
    }
    hasNext(): boolean {
        if (this.position < this.collection.length){
            return true;
        } else {
            return false;
        }
    }
}
class Numbers implements IAggregator {
    private collection: number[] = [];
    constructor(collection: number[]){
        this.collection = collection;
    }
    public createIterator(): IIterator {
        return new ConcreteIterator(this.collection);
    }
}
const fibArray = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89];
let numbers: Numbers = new Numbers(fibArray);
let it: ConcreteIterator = numbers.createIterator();
while (it.hasNext()){
    console.log(it.next());
}
                        
                    

mediator: defines simplified communication between classes

                    
interface IMediator {
    send(msg: string, colleague: Colleague): void;
}
class Colleague {
        public mediator: IMediator;
        constructor(mediator: IMediator){
            this.mediator = mediator;
        }
        public send(msg: string): void {
            throw new Error("abstract method");
        }
        public receive(msg: string): void {
            throw new Error("abstract method");
        }
}
class ConcreteColleagueA extends Colleague {
        constructor(mediator: IMediator){
            super(mediator);
        }
        public send(msg: string): void {
            this.mediator.send(msg, this);
        }
        public receive(msg: string): void {
            console.log(`${msg} receive of concretecolleagueA is being invoked`);
        }
}
class ConcreteColleagueB extends Colleague {
        constructor(mediator: IMediator){
            super(mediator);
        }
        public send(msg: string): void {
            this.mediator.send(msg, this);
        }
        public receive(msg: string): void {
            console.log(`${msg} receive of concretecolleagueB is being invoked`);
        }
}
class ConcreteMediator implements IMediator {
        public concreteColleagueA: ConcreteColleagueA;
        public concreteColleagueB: ConcreteColleagueB;
        public send(msg: string, colleague: Colleague): void {
            if (this.concreteColleagueA === colleague){
                this.concreteColleagueB.receive(msg);
            } else {
                this.concreteColleagueA.receive(msg);
            }
        }
}
let cm: ConcreteMediator = new ConcreteMediator();
let c1: ConcreteColleagueA = new ConcreteColleagueA(cm);
let c2: ConcreteColleagueB = new ConcreteColleagueB(cm);
cm.concreteColleagueA = c1;
cm.concreteColleagueB = c2;
c1.send("send of concretecolleaguea is being invoked");
c2.send("send of concretecolleagueb is being invoked");
                        
                    

memento: capture and restore an object's internal state

                    
class State {
  private str: string;
  constructor(str: string) { this.str = str; }
  get Str(): string { return this.str; }
  set Str(str: string) { this.str = str; }
}
class Originator {
  private state: State;
  constructor(state: State) { this.state = state; }
  get State(): State { return this.state; }
  set State(state: State) {
    this.state = state;
    console.log('state: ', state);
  }
  public createMemento(): Memento {
    console.log('creates a memento with a given state');
    return new Memento(this.state);
  }
  public setMemento(memento: Memento) {
    console.log('sets the state back');
    this.State = memento.State;
  }
}
class Memento {
  private state: State;
  constructor(state: State) { this.state = state; }
  get State(): State {
    console.log('get memento\'s state');
    return this.state;
  }
}
class CareTaker {
  private memento: Memento;
  get Memento(): Memento { return this.memento; }
  set Memento(memento: Memento) { this.memento = memento; }
}
let state: State = new State("... original state");
let originator: Originator = new Originator(state);
let careTaker: CareTaker = new CareTaker();
careTaker.Memento = originator.createMemento();
originator.State = new State("new state ...");
originator.setMemento(careTaker.Memento);
                        
                    

observer: a way of notifying change to a number of classes

                    
class Subject {
    private observers: Observer[] = [];
    public register(observer: Observer): void {
        console.log(observer, " is pushed");
        this.observers.push(observer);
    }
    public unregister(observer: Observer): void {
        const n: number = this.observers.indexOf(observer);
        console.log(observer, " is removed");
        this.observers.splice(n, 1);
    }
    public notify(): void {
        console.log("notify all observers ", this.observers);
        let i: number;
        let max: number = 0;
        for( i = 0, max = this.observers.length; i < max; i += 1 ){
            this.observers[i].notify();
        }
    }
}
class ConcreteSubject extends Subject {
        private subjectState: number;
        get SubjectState(): number {
            return this.subjectState;
        }
        set SubjectState(subjectState: number){
            this.subjectState = subjectState;
        }
}
class Observer {
        public notify(): void {
            throw new Error("abstract method");
        }
}
class ConcreteObserver extends Observer {
        private name: string;
        private state: number;
        private subject: ConcreteSubject;
        constructor(subject: ConcreteSubject, name: string){
            super();
            console.log("concreteobserver ", name, " is created" );
            this.subject = subject;
            this.name = name;
        }
        public notify(): void {
            this.state = this.subject.SubjectState;
            console.log("concreteobservers notify method");
            console.log(this.name, this.state);
            
        }
        get Subject(): ConcreteSubject {
            return this.subject;
        }
        set Subject(subject: ConcreteSubject){
            this.subject = subject;
        }
}
let subject: ConcreteSubject = new ConcreteSubject();
subject.register(new ConcreteObserver(subject, "flash"));
subject.register(new ConcreteObserver(subject, "wonderwoman"));
subject.register(new ConcreteObserver(subject, "spiderman"));
subject.SubjectState = 1001;
subject.notify();
                        
                    

state: alter an object's behavior when its state changes

                    
interface State {
    handle(context: Context): void;
}
class ConcreteStateA implements State {
    public handle(context: Context): void {
        console.log("handle method of concretestatea is being invoked");
        context.State = new ConcreteStateB();
    }
}
class ConcreteStateB implements State {
    public handle(context: Context): void {
        console.log("handle method of concretestateb is being invoked");
        context.State = new ConcreteStateA();
    }
}
class Context {
        private state: State;
        constructor(state: State){
            this.state = state;
        }
        get State(): State {
            return this.state;
        }
        set State(state: State){
            this.state = state;
        }
        public request(): void {
            console.log("request is being invoked");
            this.state.handle(this);
        }
}
let context: Context = new Context(new ConcreteStateA());
for (let i = 0; i < 8; i += 1){
    context.request();
}
                        
                    

strategy: encapsulates an algorithm inside a class

                    
interface IStrategy {
    execute(): void;
}
class ConcreteStrategy1 implements IStrategy {
    public execute(): void {
        console.log("execute method of concretestrategy1 is being invoked");
    }
}
class ConcreteStrategy2 implements IStrategy {
    public execute(): void {
        console.log("execute method of concretestrategy2 is being invoked");
    }
}
class ConcreteStrategy3 implements IStrategy {
    public execute(): void {
        console.log("execute method of concretestrategy3 is being invoked");
    }
}
class Context {
    private strategy: IStrategy;
    constructor(strategy: IStrategy){
        this.strategy = strategy;
    }
    public executeStrategy(): void {
        this.strategy.execute();
    }
}
let context: Context = new Context(new ConcreteStrategy1());
context.executeStrategy();
context = new Context(new ConcreteStrategy2());
context.executeStrategy();
context = new Context(new ConcreteStrategy3());
context.executeStrategy();
                        
                    

template method: defer the exact steps of an algorithm to a subclass

                    
 class AbstractClass {
    public method1(): void {
        throw new Error("abstract method1");
    }
    public method2(): void {
        throw new Error("abstract method2");
    }
    public method3(): void {
        throw new Error("abstract method3");
    }
    public templateMethod(): void {
        console.log("templatemethod is being invoked");
        this.method1();
        this.method2();
        this.method3();
    }
}
class ConcreteClass1 extends AbstractClass {
    public method1(): void {
        console.log("method1 of concreteclass1 invoked");
    }
    public method2(): void {
        console.log("method2 of concreteclass1 invoked");
    }
    public method3(): void {
        console.log("method3 of concreteclass1 invoked");
    }
}
class ConcreteClass2 extends AbstractClass {
    public method1(): void {
        console.log("method1 of concreteclass2 invoked");
    }
    public method2(): void {
        console.log("method2 of concreteclass2 invoked");
    }
    public method3(): void {
        console.log("method3 of concreteclass2 invoked");
    }
}
let cc1: ConcreteClass1 = new ConcreteClass1();
cc1.templateMethod();
let cc2: ConcreteClass2 = new ConcreteClass2();
cc2.templateMethod();
                        
                    

visitor: defines a new operation to a class without change

                    
interface IVisitor {
    visitConcreteElement1(concreteElement1: ConcreteElement1): void;
    visitConcreteElement2(concreteElement2: ConcreteElement2): void;
} 
class ConcreteVisitor1 implements IVisitor {
        public visitConcreteElement1(concreteElement1: ConcreteElement1): void {
            console.log("visitconcreteelement1 of concretevisitor1 invoked");
        }
        public visitConcreteElement2(concreteElement2: ConcreteElement2): void {
            console.log("visitconcreteelement2 of concretevisitor1 invoked");
        }
}
class ConcreteVisitor2 implements IVisitor {
        public visitConcreteElement1(concreteElement1: ConcreteElement1): void {
            console.log("visitconcreteelement1 of concretevisitor2 invoked");
        }
        public visitConcreteElement2(concreteElement2: ConcreteElement2): void {
            console.log("visitconcreteelement2 of concretevisitor2 invoked");
        }
}
interface Element {
    operate(visitor: IVisitor): void;
}
class ConcreteElement1 {
        public operate(ivisitor: IVisitor): void {
            console.log("operate of concreteelement1 invoked");
            ivisitor.visitConcreteElement1(this);
        }
}
class ConcreteElement2 {
        public operate(ivisitor: IVisitor): void {
            console.log("operate of concreteelement2 invoked");
            ivisitor.visitConcreteElement2(this);
        }
}
class Objs {
    private elements: Element[] = [];
    public attach(e: Element): void {
        this.elements.push(e);
    }
    public detach(e: Element): void {
        let index = this.elements.indexOf(e);
        this.elements.splice(index, 1);
    }
    public operate(ivisitor: IVisitor){
        let i = 0;
        let max = this.elements.length;
        for (; i < max; i += 1){
            this.elements[i].operate(ivisitor);
        }
    }
}
let objs: Objs = new Objs();
objs.attach( new ConcreteElement1());
objs.attach( new ConcreteElement2());
let cv1: ConcreteVisitor1 = new ConcreteVisitor1();
let cv2: ConcreteVisitor2 = new ConcreteVisitor2();
objs.operate(cv1);
objs.operate(cv2);
                        
                    

Default Success Warning Important Info Inverse
B S W I ? O