Home > database >  Is it possible to abstract the singleton pattern from the class itself?
Is it possible to abstract the singleton pattern from the class itself?

Time:11-03

Working with typescript/javascript I have built a collection of singletons.

However, in an attempt to refactor and abstract the implementation, I tried to prepare a class which will implement that singleton logic and for them to extend. This was apparently naive of me, since the situation leads to constructor problems and seems to be unfeasible.

So my question, is it possible to elegantly apply singleton behavior on a collection of classes / objects?

In the following code, just out of readability it would be nice to abstract away the singleton logic. Further, specifically, I would like to coerce other classes in this collection, as they are added by contributors, to implement them as singletons. It would be a little nicer to do that by just "pluggin in" some abstracted extended class or some sort of wrapper.

class Car {
  /* Singleton Implementation Start */
  private static _instance: Car;

  private constructor() {}

  public static getInstance(): Car {
    if (!Car._instance) {
      Car._instance = new Car();
    }
    return Car._instance;
  }
  /* Singleton Implementation End */

  private _name = "Audi"
  private _color = 'red';
  private _maxSpeed = 150;

  drive(){
    console.log(`WOW! You are riding your cool ${this._color} ${this._name} at ${150} kmh!` )
  }

  paint(color: string){
    this._color = color;
  }
}

class Plane {
  /* Singleton Implementation Start */
  private static _instance: Plane;

  private constructor() {}

  public static getInstance(): Plane {
    if (!Plane._instance) {
      Plane._instance = new Plane();
    }
    return Plane._instance;
  }
  /* Singleton Implementation End */

  private _motors = 4;
  private _maxSpeed = 550;

  fly(){
    console.log(`WOW! You are flying your plane with roaring ${_motors} motors at ${150} kmh!` )
  }
}

CodePudding user response:

If we create a factory function that takes a generic:

function Singleton<T>() {
    return class Singleton {
        static instance: T; // using native private fields

        protected constructor() {}

        public static getInstance(): T {
            if (!this.instance) this.instance = new this() as T;

            return this.instance;
        }
    }
}

Then we can extend the class returned and provide the generic as the class itself:

class Car extends Singleton<Car>() {
  private _name = "Audi"
  private _color = 'red';
  private _maxSpeed = 150;

  drive(){
    console.log(`WOW! You are riding your cool ${this._color} ${this._name} at ${150} kmh!` )
  }

  paint(color: string){
    this._color = color;
  }
}

This has to be done with a function since static members cannot reference type parameters of the class. Also, a downside is that you can't have any private or protected members.

Playground

  • Related