What is a SOLID way for creating objects dynamically using a factory?

Description I want to create an instance of an object at runtime, however, the parameters used to create the object are not always the same. To help explain, I have created a simplified example (in TypeScript). In this example, I want to create a instance of a Rectangle class at runtime. Depending on where in the code the rectangle is created, a different set of parameters may be used to create the rectangle. Initial Solution Initially, I created a single factory class that was responsible for creating a rectangle. This class can then be injected as needed to create a rectangle at runtime. Code Point export class Point { private readonly _x: number; private readonly _y: number; public constructor(x: number, y: number) { this._x = x; this._y = y; } public get x(): number { return this._x; } public get y(): number { return this._y; } } Rectangle export class Rectangle { private readonly _width: number; private readonly _height: number; public constructor(width: number, height: number) { this._width = width; this._height = height; } public get width(): number { return this._width; } public get height(): number { return this._height; } } Rectangle Factory export class RectangleFactory { public createFromWidthAndHeight(width: number, height: number): Rectangle { return new Rectangle(width, height); } public createFromPoints(p1: Point, p2: Point): Rectangle { let width: number = Math.abs(p1.x - p2.x); let height: number = Math.abs(p1.y - p2.y); if (width == 0 || height == 0) return null; return new Rectangle(width, height); } public createFromRectangle(r: Rectangle): Rectangle { return new Rectangle(r.width, r.height); } } Should I want to create a rectangle some new way, like from edges or given a width and an area, etc... I would need to continue expanding this factory class, which violates SOLID and could lead to quite a lengthy class. Improved Solution? An approach I took to make the factory more open/closed was to create a parameter object (or interface), which has the option of storing any parameter that may be used by a factory method to create the rectangle (IRectangleFactoryInput). Parameter Object export interface IRectangleFactoryInput { width?: number; height?: number; p1?: Point; p2?: Point; rectangle?: Rectangle; } Then I created a rectangle factory interface, which takes the parameter object and returns a rectangle. Rectangle Factory Interface export interface IRectangleFactory { create(input: IRectangleFactoryInput): Rectangle } Finally, I created separate classes for each method used to create a rectangle. With this approach, if I need to create a rectangle some other way, I would add parameters to the parameter object, and create a new factory class that implements the IRectangleFactory interface. The IRectangleFactory can be injected as needed. Rectangle from Width and Height export class RectangleFromWidthAndHeight implements IRectangleFactory { create(input: IRectangleFactoryInput): Rectangle { if (input.width == null || input.height == null) return; return new Rectangle(input.width, input.height); } } Rectangle from Points export class RectangleFromPoints implements IRectangleFactory { create(input: IRectangleFactoryInput): Rectangle { if (input.p1 == null || input.p2 == null) return let width: number = Math.abs(input.p1.x - input.p2.x); let height: number = Math.abs(input.p1.y - input.p2.y); if (width == 0 || height == 0) return null; return new Rectangle(width, height); } } Rectangle from Rectangle export class RectangleFromRectangle implements IRectangleFactory { create(input: IRectangleFactoryInput): Rectangle { if (input.rectangle == null) return; return new Rectangle(input.rectangle.width, input.rectangle.height); } } I understand this approach would potentially lead to a rather large parameter object. But I'm thinking that would be far less messy than a rather large factory class. Questions Is there a better/cleaner way to go about creating instances dynamically using a factory? Are there any major drawbacks / flaws with my "improved approach"?

Feb 28, 2025 - 18:25
 0
What is a SOLID way for creating objects dynamically using a factory?

Description

I want to create an instance of an object at runtime, however, the parameters used to create the object are not always the same. To help explain, I have created a simplified example (in TypeScript).

In this example, I want to create a instance of a Rectangle class at runtime. Depending on where in the code the rectangle is created, a different set of parameters may be used to create the rectangle.

Initial Solution

Initially, I created a single factory class that was responsible for creating a rectangle. This class can then be injected as needed to create a rectangle at runtime.

Code

Point

export class Point {
    private readonly _x: number;
    private readonly _y: number;

    public constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }

    public get x(): number {
        return this._x;
    }

    public get y(): number {
        return this._y;
    }
}

Rectangle

export class Rectangle {
    private readonly _width: number;
    private readonly _height: number;

    public constructor(width: number, height: number) {
        this._width = width;
        this._height = height;
    }

    public get width(): number {
        return this._width;
    }

    public get height(): number {
        return this._height;
    }
}

Rectangle Factory

export class RectangleFactory {
    public createFromWidthAndHeight(width: number, height: number): Rectangle {
        return new Rectangle(width, height);
    }

    public createFromPoints(p1: Point, p2: Point): Rectangle {
        let width: number = Math.abs(p1.x - p2.x);
        let height: number = Math.abs(p1.y - p2.y);

        if (width == 0 || height == 0) return null;

        return new Rectangle(width, height);
    }

    public createFromRectangle(r: Rectangle): Rectangle {
        return new Rectangle(r.width, r.height);
    }
}

Should I want to create a rectangle some new way, like from edges or given a width and an area, etc... I would need to continue expanding this factory class, which violates SOLID and could lead to quite a lengthy class.

Improved Solution?

An approach I took to make the factory more open/closed was to create a parameter object (or interface), which has the option of storing any parameter that may be used by a factory method to create the rectangle (IRectangleFactoryInput).

Parameter Object

export interface IRectangleFactoryInput {
    width?: number;
    height?: number;
    p1?: Point;
    p2?: Point;
    rectangle?: Rectangle;
}

Then I created a rectangle factory interface, which takes the parameter object and returns a rectangle.

Rectangle Factory Interface

export interface IRectangleFactory {
    create(input: IRectangleFactoryInput): Rectangle
}

Finally, I created separate classes for each method used to create a rectangle. With this approach, if I need to create a rectangle some other way, I would add parameters to the parameter object, and create a new factory class that implements the IRectangleFactory interface. The IRectangleFactory can be injected as needed.

Rectangle from Width and Height

export class RectangleFromWidthAndHeight implements IRectangleFactory {
    create(input: IRectangleFactoryInput): Rectangle {
        if (input.width == null || input.height == null) return;

        return new Rectangle(input.width, input.height);
    }
}

Rectangle from Points

export class RectangleFromPoints implements IRectangleFactory {
    create(input: IRectangleFactoryInput): Rectangle {
        if (input.p1 == null || input.p2 == null) return

        let width: number = Math.abs(input.p1.x - input.p2.x);
        let height: number = Math.abs(input.p1.y - input.p2.y);

        if (width == 0 || height == 0) return null;

        return new Rectangle(width, height);
    }
}

Rectangle from Rectangle

export class RectangleFromRectangle implements IRectangleFactory {
    create(input: IRectangleFactoryInput): Rectangle {
        if (input.rectangle == null) return;

        return new Rectangle(input.rectangle.width, input.rectangle.height);
    }
}

I understand this approach would potentially lead to a rather large parameter object. But I'm thinking that would be far less messy than a rather large factory class.

Questions

  1. Is there a better/cleaner way to go about creating instances dynamically using a factory?
  2. Are there any major drawbacks / flaws with my "improved approach"?