Polymorphism on a REST service

Viewed 53

I am trying to clean and refactor my service code which currently looks like this-

public void generateBalance(Receipt receipt) {

if (receipt.getType().equals(X) && receipt.getRegion.equals(EMEA)) {

// do something to the receipt that's passed 

} else if (receiptType.equals(Y)) {

// do something to the receipt 

} else if (receipt.getRegion.equals(APAC) {
// call an external API and update the receipt
}....

...
// finally

dataStore.save(receipt);

Basically there's a bunch of conditionals that are in this main service which look for certain fields in the object that is being passed. Either it's the type or the region.

I was looking to use this design pattern- https://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html However, I am not sure how this would work for a service class. Currently my REST handler calls this particular service. Also how can I do polymorphism for both the "receiptType" and "region"?

Is there a way I can just do all the updates to the receipt once in different services, then finally save the receipt at one location? (maybe a base class?) I am really confused on how to start. TIA!

1 Answers

If your classes should have the same behaviour, then it becomes pretty simple to use polymorpism. The pattern is called as Strategy. Let me show an example.

At first we need to use enum. If you do not have enum, then you can create a method which will return enum value based on your conditions:

if (receipt.getType().equals(X) && receipt.getRegion.equals(EMEA)) // other 
     // code is omitted for the brevity

So enum will look like this:

public enum ReceiptType
{
    Emea, Y, Apac
}

Then we need an abstract class which will describe behaviour for derived classes:

public abstract class ActionReceipt
{
    public abstract string Do();
}

And our derived classes will look this:

public class ActionReceiptEmea : ActionReceipt
{
    public override string Do()
    {
        return "I am Emea";
    }
}

public class ActionReceiptY : ActionReceipt
{
    public override string Do()
    {
        return "I am Y";
    }
}

public class ActionReceiptApac : ActionReceipt
{
    public override string Do()
    {
        return "I am Apac";
    }
}

Moreover, we need a factory which will create derived classes based on enum. So we can use Factory pattern with a slight modification:

public class ActionReceiptFactory
{
    private Dictionary<ReceiptType, ActionReceipt> _actionReceiptByType =  
        new Dictionary<ReceiptType, ActionReceipt>
    {
        {
            ReceiptType.Apac, new ActionReceiptApac()
        },
        {
            ReceiptType.Emea, new ActionReceiptEmea()
        },
        {
            ReceiptType.Y, new ActionReceiptY()
        }
    };

    public ActionReceipt GetInstanceByReceiptType(ReceiptType receiptType) =>
        _actionReceiptByType[receiptType];
}

And then polymorpism in action will look like this:

void DoSomething(ReceiptType receiptType) 
{
    ActionReceiptFactory actionReceiptFactory = new ActionReceiptFactory();
    ActionReceipt receipt = 
        actionReceiptFactory.GetInstanceByReceiptType(receiptType);
    string someDoing = receipt.Do(); // Output: "I am Emea"
}

UPDATE:

You can create some helper method which will return enum value based on your logic of region and receiptType:

public class ReceiptTypeHelper
{
    public ReceiptType Get(ActionReceipt actionReceipt) 
    {
        if (actionReceipt.GetType().Equals("Emea"))
            return ReceiptType.Emea;
        else if (actionReceipt.GetType().Equals("Y"))
            return ReceiptType.Y;

        return ReceiptType.Apac;
    }
}

and you can call it like this:

void DoSomething()
{
    ReceiptTypeHelper receiptTypeHelper = new ReceiptTypeHelper();
    ReceiptType receiptType = receiptTypeHelper
        .Get(new ActionReceiptEmea());

    ActionReceiptFactory actionReceiptFactory = new 
        ActionReceiptFactory();
    ActionReceipt receipt =
        actionReceiptFactory.GetInstanceByReceiptType(receiptType);
    string someDoing = receipt.Do(); // Output: "I am Emea"
}
Related