Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design Pattern to track partial results of a complex process

I'm facing a programming problem that I don't know how to solve in a object oriented and flexible way. I have in mind some bad solutions, but I'm searching for a good one. I develop in Java, so I prefer Java ideas, but any object oriented idea is welcome.

I've been searching for ideas, design patterns, or some algorithm that can help me, but I don't know which terminology or name give to my problem, so I couldn't find any clue.

Problem:

Summary:

I need to track partial results of a process that makes different changes to a collection of entities. I need this to report to the user, the detail of each calculation "step" in a table report. And also I need to persist this collection in a database.

Detail:

The software I maintain has an entity similar to this one:

public class Salary {
    private Date date;
    private BigDecimal amount;
}

That is grouped in a Collection, like this:

List<Salary> thisYearSalaries;

This set of entities can be modified by a set of "tasks" depending on some rules:

  • Apply a certain tax (from a set of differents taxes)
  • Calculate a Future value for the money amount (More Info)
  • Discount an amount of money to keep below a maximum
  • etc...

For example:

public void processSalaries(List<Salary> theSalaries) {
    applyTax(theSalaries, Taxes.TAX_TYPE_1);
    (...)
    getFutureValue(theSalaries, someFutureDate);
    (...)
    restrictToAMaximum(theSalaries, Maximum.MARRIED_MAXIMUM);
    (...)
    applyTax(theSalaries, TAXES.TAX_TYPE_3);
    (...)
}

public void applyTax(List<Salary> theSalaries, Tax taxToApply) {
    for(Salary aSalary : theSalaries) {
        aSalary.setAmount(calculateAmountWithTax(aSalary.getAmount(), taxToApply);
    }
}

(...)

What I need is to process this collection of salaries, making changes to the amount of money but preserving all the intermediate "states" of the money amount, to show it to the user in a table with columns like these:

Example Report: (the question is about the first 4 rows of data only, don't pay attention on the rest)

Report Example

My Ideas:

Add an attribute for each "partial result" on the class Salary

public class Salary {
    (...)
    private BigDecimal originalAmount;
    private BigDecimal amountAfterFirstTax;
    private BigDecimal amountAfterMaximumRestriction;
    (...)
}

Problems:

  • The "steps" aren't rigid, maybe tomorrow one "step" changes, a new one appears, or the "meaning" of some step changes. In that case I will need to refactor the code too much.
  • Some "steps" can be repeated, so, how can I tell to the "method" in which attribute have to "set" the result of the calculation?

Add a HashMap to the Salary class where I can put the partial results, and pass to the "step" method the "key" where it have to put the partial result

public class Salary {
    (...)
    HashMap<String, BigDecimal> partialResults;
    (...)
}

Problems:

  • In some place I need to populate the HashMap to a JPA entity to save it on my Database
  • If another developer changes the name of the key (for whatever reason) maybe the "populating" of the attributes gets broken

Final Note: There are other similar situations with other "similar" entities in my application, so it would be great if we can find a general solution for this :D


Edit: New doubts about how to model the data to persist it

All the ideas are similar and really useful. All of them are related to the Command Pattern and I think that are great solutions. But now I have some new doubts:

With a higher level of abstraction, my application does something like this:

  1. The user enter the list of salaries
  2. The app process this salaries through different "steps"
  3. With the LAST salaries amount it calculates a mean and continues with other calculations
  4. After some screens and some processing, I show a report to the user with the FINAL salaries amount, and all the intermediate steps. And then I save this intermediate information for audit purpose

So, the second step is almost solved. I would use something like the Command Pattern. My problem now is how can I model the data to persist it in a relational database. Because, besides the partial "result" of each operation, there is more information that I don't need to show to the user, but I need to store it in a database.

My idea is something like this:

Salaries Table:

id
date // The date of the Salary
finalAmount // The final amount after all the calculations ends

partialResults Table:

id
salaryId // The id of the salary which this element represent a partial result
amount // Partial amount
operationCode // Type of operation
operationDescription 

But the problem is, my "future value" operation has, as an output, this information:

  • Date of the future value
  • Coefficient used for update the value
  • Partial amount

But "apply tax" operation has different information:

  • % Tax applied
  • Partial Amount

So, how can I save different "output" information for different operations?

like image 388
Matyf Avatar asked Sep 14 '13 00:09

Matyf


2 Answers

I would code the "Operations" you do as objects. You have the data, when you apply an operation to it, that operation is an object. the operation object holds some information of it's own (Perhaps a starting and ending value) and it knows how to format that value for output.

When you start, you a queue of these operations--each one slightly different (possibly different classes, possibly just with internal state differences when they are created). You combine this queue with your data and pass the data from one piece in the queue to the next, or you can think of it as iterating over the queue and applying each operation to the data (which you probably pass in).

When you are done, the operation objects each remember the history of what they did and know how to format it (for instance, "deducted %s percent for tax") and your data has been mutated to the correct value.

This can also give you "Unwinds" where you could apply the pattern in the reverse order to back up to a previous value (Which probably has no interest to you here, but in some cases can be amazing).

I believe this is called the "Command" pattern. It's terribly useful in any case.

The other nice thing--the group of operations you place into the queue can be selected with data or any number of sources allowing you to adjust your calculations without any code changes in many cases.

The classes would be terribly simple, something like:

public class TaxOperation
{
    private double rate;
    private double amountTaxed;

    public TaxOperation(double rate)
    {
        this.rate=rate;
    }

    @Override
    public double getValue(double initial)
    {
        amountTaxed = initial * rate;
        return initial - amountTaxed;
    }

    @Override
    public String getDescription()
    {
        return "A tax of "+rate+" was applied which deducted "+amountTaxed
    }
}

This seems really simple, and it is, but sometimes the simple classes do the most. Obviously the getValue operation isn't what you want, but you should be able to see how to derive what you want using that pattern.

Also note that an "Operation" can do anything--for instance it can be amazingly good for threading (Imagine your operations take seconds or minutes, you just submit a queue of them to a processor and let them go).

like image 160
Bill K Avatar answered Sep 17 '22 01:09

Bill K


You can create a class named SalaryReport which is composed of many objects of SalaryReportRow.

In a SalaryReport you have a method name calculate() which executes all the algorithms. SalaryReportRow can be specialized in many type of rows that suit your needs.

class SalaryReport {

    private List<SalaryReportRow> rows;
    private List<SalaryCalculator> calculators;

    private HashMap<SalaryCalculator,List<SalaryReportRow>> results;

    ...

    public float getResults() {

    }

    public void applyCalculators(){
        for(SalaryCalculator s : calculators){
            results.put(s,s.execute(rows));
        }
    }

    public void setSalaryCalculators(List<SalaryCalculator> calculators){
        this.calculators = calculators;
    }

    public float getCalculation(SalaryCalculator s){
            return results.get(s);
    }
    ...

}

When calculating taxes you can use a Strategy Pattern to mantain your code more flexible. If you have to combine many strategies you can use also a Composite Pattern with Strategy or a Chain of Responsibility with Strategy.

abstract class SalaryCalculator implements Calculator {

        //Order of children matters
    protected List<SalaryCalculator> children = new ArrayList<SalaryCalculator>;

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){

        List<SalaryReportRow> result_rows = new ArrayList<SalaryReportRow> (rows);

        for(SalaryCalculator s: children){
            result_rows = s.execute(result_rows);
        }

        return result_rows;
    }

}

class TaxCalculator extends SalaryCalculator {

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){
        List<SalaryReportRow> result_rows = super.execute(rows);

        //
    }

}


class MaxSalaryCalculator extends SalaryCalculator {

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){
        List<SalaryReportRow> result_rows = super.execute(rows);

        ...
    }

}

class MaxSalaryWithTaxCalculator extends SalaryCalculator {

    public MaxSalaryWithTaxCalculator() {
        children.add(new MaxSalaryCalculator());
        children.add(new TaxCalculator());
    }

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){
        List<SalaryReportRow> result_rows = super.execute(rows);

        ...
    }
}

So the SalaryReport will have a method called setSalaryCalculators(List) (you can use also more than one algorithms at once), where List is a list that contains all the strategies applied.

You will have more SalaryReportRows:

  1. One for taxes.
  2. One for money earned.
  3. ...

So you can easily calculate all steps.

When you communicate with storage handlers I suggest to use a DAO pattern with interfaces which is responsible of creating and saving objects.

I hope this will be helpful. :)

like image 42
Idipaolo Avatar answered Sep 21 '22 01:09

Idipaolo