The Abstract Factory design pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. Below is a Java implementation of the Abstract Factory design pattern.
Implementation of Abstract Factory Pattern
- Abstract Factory Interface: Defines methods for creating abstract products.
- Concrete Factories: Implement the creation of specific families of products.
- Abstract Product Interfaces: Define the types of products.
- Concrete Products: Implement the abstract product interfaces.
- Client Code: Uses the factory and products through their interfaces.
Example 1: Abstract Factory with Interfaces in Java
Below is an example implementation of the Abstract Factory pattern using interfaces in a banking context for creating different types of loans.
// Abstract Factory
interface LoanFactory {
    Loan createLoan();
}
// Abstract Product
interface Loan {
    void getDetails();
}
// Concrete Products
class MortgageLoan implements Loan {
    @Override
    public void getDetails() {
        System.out.println("This is a Mortgage Loan.");
    }
}
class PersonalLoan implements Loan {
    @Override
    public void getDetails() {
        System.out.println("This is a Personal Loan.");
    }
}
class AutoLoan implements Loan {
    @Override
    public void getDetails() {
        System.out.println("This is an Auto Loan.");
    }
}
// Concrete Factories
class MortgageLoanFactory implements LoanFactory {
    @Override
    public Loan createLoan() {
        return new MortgageLoan();
    }
}
class PersonalLoanFactory implements LoanFactory {
    @Override
    public Loan createLoan() {
        return new PersonalLoan();
    }
}
class AutoLoanFactory implements LoanFactory {
    @Override
    public Loan createLoan() {
        return new AutoLoan();
    }
}
// Client
public class BankingApplication {
    public static void main(String[] args) {
        LoanFactory mortgageFactory = new MortgageLoanFactory();
        Loan mortgageLoan = mortgageFactory.createLoan();
        mortgageLoan.getDetails();
        LoanFactory personalFactory = new PersonalLoanFactory();
        Loan personalLoan = personalFactory.createLoan();
        personalLoan.getDetails();
        LoanFactory autoFactory = new AutoLoanFactory();
        Loan autoLoan = autoFactory.createLoan();
        autoLoan.getDetails();
    }
}
Example 2: Abstract Factory with Enumerations in Java
This example uses enums to manage the loan creation logic. Each enum instance acts as a factory.
// Abstract Product
abstract class Loan {
    abstract void getDetails();
}
// Concrete Products
class MortgageLoan extends Loan {
    @Override
    void getDetails() {
        System.out.println("This is a Mortgage Loan.");
    }
}
class PersonalLoan extends Loan {
    @Override
    void getDetails() {
        System.out.println("This is a Personal Loan.");
    }
}
class AutoLoan extends Loan {
    @Override
    void getDetails() {
        System.out.println("This is an Auto Loan.");
    }
}
// Abstract Factory using Enum
enum LoanFactory {
    MORTGAGE {
        @Override
        public Loan createLoan() {
            return new MortgageLoan();
        }
    },
    PERSONAL {
        @Override
        public Loan createLoan() {
            return new PersonalLoan();
        }
    },
    AUTO {
        @Override
        public Loan createLoan() {
            return new AutoLoan();
        }
    };
    public abstract Loan createLoan();
}
// Client
public class BankingApplicationWithEnum {
    public static void main(String[] args) {
        Loan mortgageLoan = LoanFactory.MORTGAGE.createLoan();
        mortgageLoan.getDetails();
        Loan personalLoan = LoanFactory.PERSONAL.createLoan();
        personalLoan.getDetails();
        Loan autoLoan = LoanFactory.AUTO.createLoan();
        autoLoan.getDetails();
    }
}
Key Points:
- 
Using Interfaces: - Provides flexibility and extensibility by allowing additional types of loans and factories without modifying existing code.
- Clear separation of concerns with individual factory classes for each loan type.
 
- 
Using Enumerations: - Simplifies factory management as enum constants inherently represent different factories.
- Reduces the overhead of creating multiple factory classes but is less flexible for large-scale systems.
 
Advantages and disadvantages of using
Advantages
- 
Encapsulation of Object Creation: - Hides the details of how objects are created and assembled, making code cleaner and easier to manage.
 
- 
Consistency: - Ensures that products created by the factory are compatible with each other (e.g., objects from the same family).
 
- 
Scalability: - Adding new product families is straightforward. You can add a new factory and corresponding products without altering existing code.
 
- 
Flexibility: - Decouples client code from concrete implementations, allowing easy swapping of product families by simply changing the factory instance.
 
- 
Promotes Dependency Inversion Principle: - Clients depend on abstractions (interfaces or abstract classes) rather than concrete classes, enhancing testability and maintainability.
 
- 
Centralized Creation Logic: - All object creation is handled in one place, making it easier to update or modify.
 
Disadvantages
- 
Complexity: - Introduces additional layers of abstraction and requires more classes and interfaces, which can make the system more complex.
 
- 
Difficult to Extend Individual Products: - Modifying or adding a single product in a family may require changes in all the factories or creating a new product family.
 
- 
Overhead for Small Systems: - In systems with limited object types or families, the pattern can be overkill and unnecessarily increase codebase size.
 
- 
Coupling Between Factories and Products: - Factories are tightly coupled to the specific product hierarchies they create, which can limit flexibility in some cases.
 
- 
Rigid Structure: - While adding a new family is straightforward, adding support for a new product across existing families can be cumbersome as it may require changes to all factories.
 
When to Use Abstract Factory
- 
Use the Abstract Factory pattern when: - The system needs to work with multiple families of related objects.
- You want to isolate the creation process of objects from their usage.
- Ensuring compatibility among objects is a priority.
- There’s a need to change the product family dynamically at runtime.
 
- 
Avoid using the pattern when: - The system has simple requirements with few types of objects.
- The overhead of creating factories and products outweighs the benefits.
 
Example 1: Abstract Factory with Interfaces in C#
Below is the implementation of the Abstract Factory pattern using interfaces in C#.
// Abstract Factory
public interface ILoanFactory
{
    ILoan CreateLoan();
}
// Abstract Product
public interface ILoan
{
    void GetDetails();
}
// Concrete Products
public class MortgageLoan : ILoan
{
    public void GetDetails()
    {
        Console.WriteLine("This is a Mortgage Loan.");
    }
}
public class PersonalLoan : ILoan
{
    public void GetDetails()
    {
        Console.WriteLine("This is a Personal Loan.");
    }
}
public class AutoLoan : ILoan
{
    public void GetDetails()
    {
        Console.WriteLine("This is an Auto Loan.");
    }
}
// Concrete Factories
public class MortgageLoanFactory : ILoanFactory
{
    public ILoan CreateLoan()
    {
        return new MortgageLoan();
    }
}
public class PersonalLoanFactory : ILoanFactory
{
    public ILoan CreateLoan()
    {
        return new PersonalLoan();
    }
}
public class AutoLoanFactory : ILoanFactory
{
    public ILoan CreateLoan()
    {
        return new AutoLoan();
    }
}
// Client
public class BankingApplication
{
    public static void Main(string[] args)
    {
        ILoanFactory mortgageFactory = new MortgageLoanFactory();
        ILoan mortgageLoan = mortgageFactory.CreateLoan();
        mortgageLoan.GetDetails();
        ILoanFactory personalFactory = new PersonalLoanFactory();
        ILoan personalLoan = personalFactory.CreateLoan();
        personalLoan.GetDetails();
        ILoanFactory autoFactory = new AutoLoanFactory();
        ILoan autoLoan = autoFactory.CreateLoan();
        autoLoan.GetDetails();
    }
}
Example 2: Abstract Factory with Enumerations in C#
This example uses enumerations to manage loan creation logic in C#.
// Abstract Product
public abstract class Loan
{
    public abstract void GetDetails();
}
// Concrete Products
public class MortgageLoan : Loan
{
    public override void GetDetails()
    {
        Console.WriteLine("This is a Mortgage Loan.");
    }
}
public class PersonalLoan : Loan
{
    public override void GetDetails()
    {
        Console.WriteLine("This is a Personal Loan.");
    }
}
public class AutoLoan : Loan
{
    public override void GetDetails()
    {
        Console.WriteLine("This is an Auto Loan.");
    }
}
// Abstract Factory using Enum
public enum LoanFactory
{
    MORTGAGE,
    PERSONAL,
    AUTO
}
public static class LoanFactoryExtensions
{
    public static Loan CreateLoan(this LoanFactory factory)
    {
        return factory switch
        {
            LoanFactory.MORTGAGE => new MortgageLoan(),
            LoanFactory.PERSONAL => new PersonalLoan(),
            LoanFactory.AUTO => new AutoLoan(),
            _ => throw new ArgumentException("Invalid Loan Type")
        };
    }
}
// Client
public class BankingApplicationWithEnum
{
    public static void Main(string[] args)
    {
        Loan mortgageLoan = LoanFactory.MORTGAGE.CreateLoan();
        mortgageLoan.GetDetails();
        Loan personalLoan = LoanFactory.PERSONAL.CreateLoan();
        personalLoan.GetDetails();
        Loan autoLoan = LoanFactory.AUTO.CreateLoan();
        autoLoan.GetDetails();
    }
}
Key Points for Both Examples:
- 
Using Interfaces: - Provides a clean separation of concerns.
- Adding new loan types requires creating new concrete product and factory classes.
 
- 
Using Enumerations: - Simplifies the logic by using a single enumto encapsulate all factory behavior.
- Extensions on the enumprovide centralized logic for creating different loan types.
- Less flexible if individual factories or additional customization is required.
 
- Simplifies the logic by using a single 
Both examples effectively demonstrate the Abstract Factory pattern in a C# context!

 

