Design a Class to Restrict Object Creation to 5 Instances

One interesting interview question that tests your understanding of object creation control is: Design a class such that only 5 objects can be created. If you try to create a 6th object, it should throw an error.

In this blog post, we’ll explore two solutions to this problem—one using a static class property and the other using a decorator pattern. Both methods effectively limit the number of objects that can be instantiated, but they approach the problem from different angles.

Let’s dive in!


Solution 1: Using a Static Class Property

In this approach, we use a static class property to keep track of the number of instances that have been created. A static property is shared across all instances of the class, which makes it the perfect mechanism to maintain a global instance count.

Code Example:

export class CustomerService {
    static instanceObject: CustomerService;
    private static MAX_INSTANCE_COUNT = 5;
    private static currentCount = 0;

    private constructor() {}

    public static getInstance() {
        this.currentCount++;
        if (this.currentCount <= this.MAX_INSTANCE_COUNT) {
            return new CustomerService();
        } else {
            throw new Error('You cannot create more instances');
        }
    }
}

Step-by-Step Breakdown:

  1. Static Properties:
   static instanceObject: CustomerService;
   private static MAX_INSTANCE_COUNT = 5;
   private static currentCount = 0;
  • instanceObject: This static property will hold the class instance.
  • MAX_INSTANCE_COUNT: This constant limits the number of objects that can be created.
  • currentCount: A static counter that tracks how many objects have been created.
  1. Private Constructor:
   private constructor() {}
  • The constructor is made private to ensure objects cannot be created using the new keyword directly.
  1. getInstance Method:
   public static getInstance() {
        this.currentCount++;
        if (this.currentCount <= this.MAX_INSTANCE_COUNT) {
            return new CustomerService();
        } else {
            throw new Error('You cannot create more instances');
        }
    }
  • Each time getInstance is called, it increments the currentCount.
  • If the currentCount is within the limit (MAX_INSTANCE_COUNT), it creates a new instance using new CustomerService().
  • If currentCount exceeds the limit, an error is thrown.

Example Usage:

const instance1 = CustomerService.getInstance(); // Success
const instance2 = CustomerService.getInstance(); // Success
//...
const instance5 = CustomerService.getInstance(); // Success
const instance6 = CustomerService.getInstance(); // Error: "You cannot create more instances"

Solution 2: Using a Decorator Pattern

In the second approach, we use a decorator to control the number of objects that can be instantiated. This is a more flexible solution as it allows you to reuse the restriction logic with different classes.

Code Example:

export function RestrictInstances(count: number) {
    return function<T extends { new (...args: any[]): {} }>(classObj: T) {
        let currentCount = 0;
        return class extends classObj {
            constructor(...args: any[]) {
                currentCount++;
                if (currentCount <= count) {
                    super(...args);
                } else {
                    throw new Error('You cannot create more objects');
                }
            }
        };
    };
}

@RestrictInstances(5)
class Sales {
    constructor(public name: string) {}
}

Step-by-Step Breakdown:

  1. Decorator Function:
   export function RestrictInstances(count: number) {
       return function<T extends { new (...args: any[]): {} }>(classObj: T) {
  • RestrictInstances is a decorator factory that accepts a count (the max number of instances allowed).
  • Inside, we return a new class that extends the original class (classObj).
  1. Instance Counter:
   let currentCount = 0;
  • This currentCount variable tracks how many objects have been instantiated.
  1. Constructor Logic:
   return class extends classObj {
       constructor(...args: any[]) {
           currentCount++;
           if (currentCount <= count) {
               super(...args);
           } else {
               throw new Error('You cannot create more objects');
           }
       }
   };
  • Each time a new object is created, the constructor increments currentCount.
  • If the currentCount exceeds the allowed limit (count), the decorator throws an error.
  1. Class Usage:
   @RestrictInstances(5)
   class Sales {
       constructor(public name: string) {}
   }
  • The @RestrictInstances(5) decorator is applied to the Sales class, restricting it to a maximum of 5 objects.

Example Usage:

const sale1 = new Sales('Deal1'); // Success
const sale2 = new Sales('Deal2'); // Success
//...
const sale5 = new Sales('Deal5'); // Success
const sale6 = new Sales('Deal6'); // Error: "You cannot create more objects"

Comparing Both Solutions

  1. Static Class Method:
  • Pros:
    • Simplicity: Easy to understand.
    • Centralized logic inside the class.
  • Cons:
    • Limited to one class.
    • The restriction logic cannot be easily reused for other classes.
  1. Decorator Pattern:
  • Pros:
    • Flexibility: You can reuse this decorator with any class, simply by applying the decorator.
    • Cleaner code: The restriction logic is separate from the class definition.
  • Cons:
    • Slightly more complex due to the use of TypeScript’s advanced decorator feature.

Conclusion

Both approaches achieve the goal of limiting the number of instances that can be created from a class. The static class method is simple and works well if you need to enforce this behavior for a single class. On the other hand, the decorator pattern is more powerful and reusable, allowing you to apply the same instance restriction logic to multiple classes.

For interviews, both solutions show a good understanding of object creation control, class-level restrictions, and more advanced TypeScript features like decorators. Try both methods out and see which one fits your style!