oroalej's avatar

Strategy vs Factory Pattern

Can someone explain what's the difference between these two patterns. I've been trying to understand how they work but for me they practically similar. Is there a rule on where to use them?

If you have any recommendation on how to make the code better, will really appreciate it.

class TransactionFactory {
  public static function initialize(CategoryTypeEnum $categoryType) {
    return match($categoryType) {
        CategoryTypeEnum::INCOME => new IncomeTransaction,
        CategoryTypeEnum::EXPENSE => new ExpenseTransaction,
        default => throw new InvalidArgumentException("Wrong category_type provided."),
    }
  }
}
class TransactionContext {
  public function __construct(protected TransactionInterface $transactionStrategy) {
  }

  public function store($request) {
    return $this->transactionStrategy->store($request);
  }
}

How to use:

class TransactionController {
  public function store(TransactionRequest $request) {
     // Strategy
     $transactionStrategy = match($categoryType) {
        CategoryTypeEnum::INCOME => new IncomeTransaction,
        CategoryTypeEnum::EXPENSE => new ExpenseTransaction,
        default => throw new InvalidArgumentException("Wrong category_type provided."),
    }
    (new TransactionContext($transactionStrategy))->store(
      TransactionData::fromRequest($request)
    );

    // Factory
    TransactionFactory::initialize($categoryType)->store(
       TransactionData::fromRequest($request)
    );
  }
}

0 likes
2 replies
LaryAI's avatar
Level 58

The Strategy and Factory patterns are both creational design patterns, but they serve different purposes.

The Strategy pattern is used to define a family of algorithms, encapsulate each one, and make them interchangeable. It allows the algorithm to vary independently from the clients that use it. In the provided code, the TransactionContext class represents the context that uses different strategies (IncomeTransaction and ExpenseTransaction) based on the CategoryTypeEnum. The TransactionContext class has a store method that delegates the actual storing logic to the selected strategy.

On the other hand, the Factory pattern is used to create objects without specifying the exact class of the object that will be created. It provides an interface for creating objects, but allows subclasses to decide which class to instantiate. In the provided code, the TransactionFactory class is responsible for creating the appropriate transaction object based on the CategoryTypeEnum. The initialize method of the TransactionFactory class returns an instance of the appropriate transaction class.

To summarize:

  • The Strategy pattern is used to define interchangeable algorithms and allows the client to select the algorithm at runtime.
  • The Factory pattern is used to create objects without specifying the exact class and allows subclasses to decide which class to instantiate.

In terms of when to use these patterns, it depends on the specific requirements of your application. The Strategy pattern is useful when you have multiple algorithms that can be used interchangeably, while the Factory pattern is useful when you want to delegate the responsibility of object creation to subclasses.

Here's an example of how you can use the Strategy and Factory patterns in the provided code:

// Strategy pattern
class TransactionContext {
  protected TransactionInterface $transactionStrategy;

  public function __construct(CategoryTypeEnum $categoryType) {
    $this->transactionStrategy = match($categoryType) {
        CategoryTypeEnum::INCOME => new IncomeTransaction,
        CategoryTypeEnum::EXPENSE => new ExpenseTransaction,
        default => throw new InvalidArgumentException("Wrong category_type provided."),
    };
  }

  public function store($request) {
    return $this->transactionStrategy->store($request);
  }
}

// Factory pattern
class TransactionFactory {
  public static function initialize(CategoryTypeEnum $categoryType) {
    return match($categoryType) {
        CategoryTypeEnum::INCOME => new IncomeTransaction,
        CategoryTypeEnum::EXPENSE => new ExpenseTransaction,
        default => throw new InvalidArgumentException("Wrong category_type provided."),
    };
  }
}

// Usage in TransactionController
class TransactionController {
  public function store(TransactionRequest $request) {
    $categoryType = getCategoryType(); // Get the category type from somewhere

    // Strategy pattern
    (new TransactionContext($categoryType))->store($request);

    // Factory pattern
    TransactionFactory::initialize($categoryType)->store($request);
  }
}

In the above example, the TransactionContext class represents the Strategy pattern, and the TransactionFactory class represents the Factory pattern. The TransactionController class demonstrates how to use both patterns.

1 like

Please or to participate in this conversation.