Singleton Pattern and ES6 Modules

Singleton Pattern

Chapter 1: Introduction

  • A singleton is a single instance of a class that can be shared throughout the entire application.

  • With regular classes, a new instance is created each time the class is imported and instantiated.

  • Singletons ensure that only one instance is used across the application.

  • Example: file1 imports a singleton counter, increments it, and the increment is reflected in other files because they all share the same instance.

  • In ES6, there are multiple ways to create singletons; one way is using a class.

Chapter 2: The New Instance

  • When instantiating a singleton class:

    • Ensure an instance doesn't already exist.

    • If an instance exists, throw an error to prevent creating another one.

    • The instance variable is set to the new instance when we instantiate it.

  • Singletons should be non-modifiable.

  • Use Object.freeze() in JavaScript to prevent adding or changing properties of the singleton object.

  • Instantiate the class as a singleton in the file before exporting it, so files import the instance, not the class itself.

Chapter 3: Import The Instance

  • Files import the instance and not the class to prevent instantiation of the already instantiated instance.

  • Freeze the instance to ensure it's not modifiable.

  • In ES6, objects can be used instead of classes for singletons.

  • Example: Create a counter object with getCount, increment, and decrement methods.

  • Freeze the object and export it for use in other files.

  • Trade-offs of singletons:

    • Good for memory management as only one instance is created.

    • Cons:

      • ES15 modules are singletons by default, reducing the need to explicitly create singletons.

      • Dependency hiding: a singleton might import another module, leading to unintended modifications.

      • Difficult to test: global changes can cause the entire test suite to fail, requiring resets after each test.

  • Singletons are useful for achieving a global state, but modules might be a better alternative.

Chapter 4: Entire Test Suite

  • Testing singletons is difficult because they change the state globally.

  • Small modifications to a singleton can cause an entire test suite to fail.

  • Singletons must be reset after each test.

  • Consider using a module if it can achieve the same goal as a singleton in managing global state.

Chapter 5: Named Connection

  • Default export: export default connection

  • When importing a default export, the name doesn't matter.

  • Example:

    • In index.js: export default connection;

    • In test.js: import connection from './index.js'; or import databaseConnection from './index.js';

  • Named export: export { connection }

  • When importing a named export, the name must match.

  • Example:

    • In index.js: export { connection };

    • In test.js: import { connection } from './index.js';

  • You can rename a named import using the as keyword: import { connection as databaseConnection } from './index.js';

Chapter 6: Called Database Connection

  • The default keyword allows exporting a value without needing to worry about naming collisions in other files.

  • You can import the default export under any name without affecting the original export name.

  • Advantage of default export: Avoid naming collisions by renaming the import, especially if the original name is already in use in the importing file.

Chapter 7: Conclusion

  • Exercise: Create a singleton database connection class.

  • Common use case: Ensuring only one database connection exists in an application.

  • Class structure:

    • DatabaseConnection class with connect and disconnect methods.

    • Ensure only one instance can ever exist.

    • Make the instance non-modifiable and a single instance using the singleton pattern.