Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a factory to create objects which use Strategy pattern?

Let's assume we have a simple payment feature on an online shop. We want to manage different transactions with different processors of transactions:

  • A transaction can be a payment or a refund.
  • A processor of transactions can be Paypal or Payplug.

So we have the following classes:

class PaymentTransaction implements Transaction {

}

class RefundTransaction implements Transaction {

}

class PaypalProcessor implements Processor {

}

class PayplugProcessor implements Processor {

}

As suggested in this answer, we could use the following class which uses Strategy and polymorphism.

class PaymentProcessor {
     private Processor processor;
     private Transaction transaction;

     public PaymentProcessor(Processor processor, Transaction transaction) {
          this.processor = processor;
          this.transaction = transaction;
     }

     public void processPayment() {
         processor.process(transaction);
     }
}

We assume the processor and the transaction to use are given from the database. I wonder how to create the PaymentProcessor object.

It seems that an abstract factory class with only one method is still a valid Abstract Factory pattern. So, in this case I wonder if using Abstract Factory would be relevant.

  • If yes, how to implement it?
  • If no, should we use Factory Method pattern with a PaymentProcessorFactory class to create PaymentProcessor with his two attributes according the details given from the database?

What is a best practice to use a factory in this case?

like image 953
Kwadz Avatar asked Feb 16 '26 16:02

Kwadz


1 Answers

We assume the processor and the transaction to use are given from the database. I wonder how to create the PaymentProcessor object.

I would define an interface that I can adapt to the database result or any other source that can provide the data needed to create a PaymentProcessor. This is also useful for unittests.

public interface PaymentProcessorFactoryArgs {
  String getProcessorType();
  String getTransactionType();
}

and then implement a factory like this.

public class PaymentProcessorFactory {

  private Map<String, Processor> processorMap = new HashMap<>();
  private Map<String, Transaction> transactionMap = new HashMap<>();

  public PaymentProcessorFactory() {
    processorMap.put("paypal", new PaypalProcessor());
    processorMap.put("payplug", new PayplugProcessor());

    transactionMap.put("refund", new RefundTransaction());
    transactionMap.put("payment", new PaymentTransaction());
  }

  public PaymentProcessor create(PaymentProcessorFactoryArgs factoryArgs) {
    String processorType = factoryArgs.getProcessorType();
    Processor processor = processorMap.get(processorType);
    if(processor == null){
      throw new IllegalArgumentException("Unknown processor type " + processorType);
    }

    String transactionType = factoryArgs.getTransactionType();
    Transaction transaction = transactionMap.get(transactionType);
    if(transaction == null){
      throw new IllegalArgumentException("Unknown transaction type " + processorType);
    }

    return new PaymentProcessor(processor, transaction);
  }
}

This is just a quick example. It would be better if you can register Processors and Transactions. E.g.

public void register(String processorType, Processor processor){
   ...
}
public void register(String transactionType, Transaction transaction){
   ...
}

You also might want to use anther type then String for the keys, maybe an enum.

In this example the Processor and Transaction objects are re-used every time a PaymentProcessor is created. If you want to create new objects for each PaymentProcessor, you can replace the Maps type

private Map<String, Factory<Processor>> processorMap = new HashMap<>();
private Map<String, Factory<Transaction>> transactionMap = new HashMap<>();

with anther factory interface. E.g.

public interface Factory<T> {
  public T newInstance();
}
like image 132
René Link Avatar answered Feb 19 '26 15:02

René Link