Welcome back! You've learned so much about creational patterns, and it's time to apply what you know to a real-world project: a banking system. In this unit, we'll focus on using creational patterns to manage and simplify the creation of banking system components.
Before we dive in, let's quickly recap the creational patterns we'll use:
- Singleton Pattern: Ensures a class has only one instance and provides a global point of access to it.
- Factory Method Pattern: Defines an interface for creating an object but lets subclasses alter the type of objects that will be created.
- Abstract Factory Pattern: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- Builder Pattern: Separates the construction of a complex object from its representation, allowing the same construction process to create various representations.
We will implement these patterns to create a logger, accounts, and account factories for our banking system.
Let's see what you'll build in this unit. You will create:
- Logger with the Singleton Pattern: A logging mechanism that ensures only one instance of the logger exists throughout the application.
- Accounts using the Factory Method Pattern: Different types of accounts (
savingsandcurrent) created via a factory method. - Account Factories using the Abstract Factory Pattern: Factories that will instantiate different types of accounts.
- Code Integration: Comprehensive integration of these patterns into a cohesive system.
First, let's create a logger using the Singleton Pattern. In TypeScript, we can use a class with a private static instance and a private constructor to ensure that only one instance of the logger exists throughout the application.
Here, the Logger class uses a private static property to hold the singleton instance. The constructor is private, so new instances cannot be created from outside the class. The getInstance method returns the single instance, creating it if it doesn't exist. The log method outputs messages.
Next, we will create different types of accounts using the Factory Method Pattern. In TypeScript, we can use abstract classes and type annotations to define a common interface for all account types.
In this example, Account is an abstract class with an abstract method accountType. The SavingsAccount and CurrentAccount classes extend Account and provide concrete implementations of the accountType method, returning a string that indicates their type.
Now, let's move on to the Abstract Factory Pattern to create account factories that will instantiate different types of accounts and associated debit cards. In TypeScript, we can use interfaces and class implementations to define the abstract factory and product types.
Here, DebitCard is an abstract class with an abstract method cardType. SavingsDebitCard and CurrentDebitCard provide concrete implementations. The AccountFactory interface defines methods for creating accounts and debit cards. SavingsAccountFactory and CurrentAccountFactory implement this interface, returning the appropriate account and debit card types.
To integrate these patterns into a cohesive system, let's see how the different components work together using TypeScript syntax and type annotations.
In this code, we create instances of SavingsAccountFactory and CurrentAccountFactory, typed as AccountFactory. We use these factories to create savingsAccount, savingsDebitCard, currentAccount, and currentDebitCard. Each component's type is logged using the singleton logger.
Here's the complete code putting everything together in TypeScript:
Understanding creational patterns in the context of a banking system not only strengthens your coding skills but also demonstrates their utility in real-world applications. These patterns help you maintain code quality by ensuring your code is clean, modular, and easy to understand. They encourage reusability, allowing you to reuse common components across different parts of the system, and enhance flexibility by enabling changes and extensions with minimal impact on existing code. By mastering these patterns, you'll be equipped to build robust and scalable systems efficiently.
