Search code examples
typescripttypesfactory-patternopen-closed-principle

How to create a factory object without violating OCP in Typescript


I just learn about a Factory Pattern and want to implement that in Typescript.

I check many sites like this and notice that all of the examples violating OCP because they have to use many if statements to find a suitable subclass constructor.

I also found a similar question of how to do that in Java. But I don't know if it could be implemented in typescript.


Solution

  • I don't know if it's a good practice but you can avoid a lot of if statement by find class by random access instead of sequentialy.

    You can try a way like this:

    enum Behaviour {
      Friendly = 0,
      Agressive
    }
    
    export abstract class BaseCharacter{
    
      protected strength: number;
    
      public canFightWith(character: BaseCharacter): Boolean{
        return this.getBehaviour() !== character.getBehaviour();
      }
    
      public getStrength():number{
        return this.strength;
      }
    
      public abstract getBehaviour():Behaviour;
    
    }
    
    export class Vilain extends BaseCharacter{
    
      private reduction;
    
      public Vilain(strength:number, reduction:number){
        this.strength = strength;
        this.reduction = reduction;
      }
    
      public getStrength():number{
        return super.getStrength()*this.reduction;
      }
    
      public getBehaviour(): Behaviour {
        return Behaviour.Agressive;
      }
    }
    
    export class Hero extends BaseCharacter{
    
      public Hero(strength:number){
        this.strength = strength
      }
    
      public getBehaviour(): Behaviour {
        return Behaviour.Friendly;
      }
    }
    
    export class StaticFactory{
    
      // in this example Hero and Vilain will extend BaseCharacter
    
      static definitions = {
        'hero':Hero,
        'vilain':Vilain
      }
    
      static instanciateCharacter(subClass: string, ...args):BaseCharacter {
        if(subClass in StaticFactory.definitions)
          return new StaticFactory.definitions[subClass](...args);
      }
    
    }
    
    export class Battle{
    
      public constructor(private first: BaseCharacter, private second: BaseCharacter){
          if(!this.first.canFightWith(second))
            throw new Error("These 2 characters fight for the same cause !");
      }
    
      public getWinner():BaseCharacter{
        if (this.first.getStrength() === this.second.getStrength())
          return !(Math.trunc(Math.random()*2))?this.first:this.second;
    
        return this.first.getStrength() > this.second.getStrength()?this.first:this.second;
    
      }
    
    }