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
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.
- Simplifies the logic by using a single
Both examples effectively demonstrate the Abstract Factory pattern in a C# context!
No hay comentarios.:
Publicar un comentario