Design Pattern Course
Abstract Factory is a creational design pattern that provides an interface for creating families of related objects without exposing their concrete classes.
A practical banking example is creating banking workflows for different account types such as Savings and Current accounts. Each account type may require a related set of services such as:
- Account creation
- Debit card generation
- Loan eligibility rules
These objects belong to the same business family because they must work consistently for a given account type.
Why Banking Is a Good Example
In real banking systems, business rules are usually grouped by product family. For example, a savings account customer may get a different card type, interest rule, and loan policy than a current account customer.
If the application directly creates classes like SavingsDebitCard, CurrentDebitCard, SavingsLoanPolicy, and CurrentLoanPolicy, the code becomes tightly coupled to product-specific implementations.
Abstract Factory solves this by allowing the client to request a full family of banking services through a single factory.
Problem Statement
Suppose a banking platform supports multiple account families:
- Savings Account
- Current Account
For each account family, the system must create related objects:
AccountDebitCardLoanPolicy
The goal is to ensure that when the client chooses SavingsBankingFactory, all related services belong to the Savings family. The same applies to Current accounts.
Product Interfaces
public interface Account { void openAccount(); }
public interface DebitCard { void issueCard(); }
public interface LoanPolicy { void showPolicy(); }
Concrete Products
Savings Family
public class SavingsAccount implements Account { @Override public void openAccount() { System.out.println("Opening Savings Account"); } }
public class SavingsDebitCard implements DebitCard { @Override public void issueCard() { System.out.println("Issuing Savings Debit Card"); } }
public class SavingsLoanPolicy implements LoanPolicy { @Override public void showPolicy() { System.out.println("Savings Account Loan Policy: lower loan eligibility"); } }
Current Family
public class CurrentAccount implements Account { @Override public void openAccount() { System.out.println("Opening Current Account"); } }
public class CurrentDebitCard implements DebitCard { @Override public void issueCard() { System.out.println("Issuing Current Debit Card"); } }
public class CurrentLoanPolicy implements LoanPolicy { @Override public void showPolicy() { System.out.println("Current Account Loan Policy: higher business loan eligibility"); } }
Abstract Factory
public interface BankingFactory { Account createAccount(); DebitCard createDebitCard(); LoanPolicy createLoanPolicy(); }
Concrete Factories
public class SavingsBankingFactory implements BankingFactory { @Override public Account createAccount() { return new SavingsAccount(); } @Override public DebitCard createDebitCard() { return new SavingsDebitCard(); } @Override public LoanPolicy createLoanPolicy() { return new SavingsLoanPolicy(); } }
public class CurrentBankingFactory implements BankingFactory { @Override public Account createAccount() { return new CurrentAccount(); } @Override public DebitCard createDebitCard() { return new CurrentDebitCard(); } @Override public LoanPolicy createLoanPolicy() { return new CurrentLoanPolicy(); } }
Client Code
public class BankingApplication { private final Account account; private final DebitCard debitCard; private final LoanPolicy loanPolicy; public BankingApplication(BankingFactory factory) { this.account = factory.createAccount(); this.debitCard = factory.createDebitCard(); this.loanPolicy = factory.createLoanPolicy(); } public void process() { account.openAccount(); debitCard.issueCard(); loanPolicy.showPolicy(); } }
public class Main { public static void main(String[] args) { BankingFactory factory = new SavingsBankingFactory(); BankingApplication app = new BankingApplication(factory); app.process(); } }
Output
Opening Savings Account Issuing Savings Debit Card Savings Account Loan Policy: lower loan eligibility
Why This Is a Correct Abstract Factory Example
This example correctly represents Abstract Factory because a single factory creates a family of related objects:
AccountDebitCardLoanPolicy
And each concrete factory creates matching variants from the same business family:
SavingsBankingFactory->SavingsAccount,SavingsDebitCard,SavingsLoanPolicyCurrentBankingFactory->CurrentAccount,CurrentDebitCard,CurrentLoanPolicy
That is the key design goal of Abstract Factory: related products should be created together in a compatible way.
Benefits
- Keeps business logic independent from concrete account implementations.
- Ensures related banking services remain consistent within a product family.
- Makes it easier to support new account families in the future.
- Improves testability because the client depends on interfaces.
Limitations
- Adds more classes and interfaces.
- Introducing a new product type such as
InsurancePolicyrequires changes in all factories. - Can be over-engineering for small applications with very simple rules.
Senior-Level Review Notes
From an experienced system design perspective, this example is better than the animal example because it reflects a business domain where product families actually matter.
However, in real banking systems, the factory usually creates service objects or strategy objects, not only raw domain objects. For example, a production-grade system may create:
- KYC validation service
- Interest calculation strategy
- Debit card issuance workflow
- Loan eligibility evaluator
That would make the example even more realistic for enterprise architecture discussions.
Best Practices
For production-grade implementation:
- Use enums like
AccountType.SAVINGSandAccountType.CURRENTinstead of raw strings. - Combine Abstract Factory with Strategy when business rules vary heavily.
- Avoid putting too much logic directly inside entities; use services where appropriate.
- Use dependency injection in Spring or other frameworks to wire the correct factory.
Interview-Ready Explanation
Abstract Factory is used when the system must create families of related objects. In banking, a savings account and a current account often have different but related services such as debit cards, loan policies, and account-opening rules. Instead of hardcoding those classes in client code, the application uses a concrete banking factory to create the correct family of related objects.