lunes, 7 de mayo de 2018

Abstract Factory design pattern with both interfaces and enumerations approaches. Examples in some popular languages.

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

  1. Abstract Factory Interface: Defines methods for creating abstract products.
  2. Concrete Factories: Implement the creation of specific families of products.
  3. Abstract Product Interfaces: Define the types of products.
  4. Concrete Products: Implement the abstract product interfaces.
  5. 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:

  1. 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.
  2. 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

  1. Encapsulation of Object Creation:

    • Hides the details of how objects are created and assembled, making code cleaner and easier to manage.
  2. Consistency:

    • Ensures that products created by the factory are compatible with each other (e.g., objects from the same family).
  3. Scalability:

    • Adding new product families is straightforward. You can add a new factory and corresponding products without altering existing code.
  4. Flexibility:

    • Decouples client code from concrete implementations, allowing easy swapping of product families by simply changing the factory instance.
  5. Promotes Dependency Inversion Principle:

    • Clients depend on abstractions (interfaces or abstract classes) rather than concrete classes, enhancing testability and maintainability.
  6. Centralized Creation Logic:

    • All object creation is handled in one place, making it easier to update or modify.

Disadvantages

  1. Complexity:

    • Introduces additional layers of abstraction and requires more classes and interfaces, which can make the system more complex.
  2. 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.
  3. Overhead for Small Systems:

    • In systems with limited object types or families, the pattern can be overkill and unnecessarily increase codebase size.
  4. Coupling Between Factories and Products:

    • Factories are tightly coupled to the specific product hierarchies they create, which can limit flexibility in some cases.
  5. 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:

  1. Using Interfaces:

    • Provides a clean separation of concerns.
    • Adding new loan types requires creating new concrete product and factory classes.
  2. Using Enumerations:

    • Simplifies the logic by using a single enum to encapsulate all factory behavior.
    • Extensions on the enum provide centralized logic for creating different loan types.
    • Less flexible if individual factories or additional customization is required.

Both examples effectively demonstrate the Abstract Factory pattern in a C# context!

 

 

 

No hay comentarios.: