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"?

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"?