4

I have a object that needs values from a grandparent object to perform a calculation. What is the best way to handle this?

Should I create a separate calculation class to get the data, perform the calculation, and return a value? Or a "parent" field, so I can traverse back up the object to get the data? Or a service this object can call to get the data from the grandparent?

Here is a simplified example.

class GrandParent{
    daysPerYear:360
    account: new Account()
}

class Account{
    percentageRate: 5,
    numberOfMonths: 12,
    paidBy = new PaidByItem()
}

class PaidByItem{
    feeAmount: 100
    totalAmount (){
        // Calculation
    }
}

To calculate this totalAmount I need data from Account and Grandparent; the calculation is totalAmount = feeAmount * numberOfMonths / daysPerYear;.

  • Could you please identate de code? – Tulains Córdova Aug 27 '16 at 16:56
  • You say there's an inheriance relationship ("parent objects") between classes but the code show no inheritance at all. – Tulains Córdova Aug 27 '16 at 17:02
  • I did not say inheritance. I am not sure how to rephrase this, its a child down a property path. – Aaron Fischer Aug 27 '16 at 17:09
3

As a basic strategy, I would almost always avoid storing the same the same piece of information in two different places/classes of a data model, that leads too easily to unmaintainable code. If you start having attributes like numberOfMonths in two classes (like it was suggested in another answer), they can deviate from each other, you might have to write code to prevent this, have to care which is the "leading" version of that attribute, might have to duplicate documentation, and so on.

One solution you suggested was "A parent field so we can traverse back up the object to get the data". That is fine when in your business domain each PaidByItem object is related somehow to one Xyz and one Aaa object. Then it makes sense to express that relationship also in code by a reference:

public class PaidByItem {
    int feeAmount = 100;
    Xyz xyz; // lets assume this is Java or C#, so a reference, no copy!
    AAA aaa;

    // ...

    public double totalAmount (){
        return  feeAmount * xyz.numberOfMonths / aaa.daysPerYear;
    }
}

Non-sensical names like Xyz or Aaa, however, don't tell us much about the real domain. There can be lots of reasons why you do not want to introduce a dependency from Xyz or Aaa into PaidByItem. For example, they could lead to a cyclic dependency you need to avoid, or these kind of relationships/references simply don't make any sense from the domain point of view. For these cases, it is often be better to use your other suggestion "A separate calculation class to get the data, perform the calc and return a value". Or, you change the design to

    public double totalAmount (int numberOfMonths, int daysPerYear){
        return  feeAmount * numberOfMonths / daysPerYear;
    }

which means the caller of this method has the responsibility to make sure the totalAmount functions is called with the right parameters from the right objects. The last option makes most sense when those parameters like numberOfMonths are not required to come from a specific, referenced object.

TLDR: avoid redundant storage, pick the solution which represents your business domain most appropriately.

  • 1
    I like this answer especially because it gives reasons why the dependencies themselves may actually be pointing the wrong way. – cbojar Aug 27 '16 at 21:02
1

I don't know what language are you using so I will write in pseudocode.

There are many ways of achieving what you want. Some solutions require contained objects to know about theair containers. Some do not. The solutions that require contained objects know about their containers are more complex so I'll go with a solution that doesn't need contained objects to have access to the state of their containers.

I guess a chain of constructors could be a solution.


public class GrandParent {
    int daysPerYear = 360;
    Account someObject = new Account(daysPerYear);

}

public class Account {

    int percentageRate = 5;
    int numberOfMonths = 12;
    int daysPerYear;
    PaidByItem paidBy;

    public Account(int daysPerYear) {
        this.daysPerYear=daysPerYear;
        paidBy = new PaidByItem(numberOfMonths,daysPerYear);
    }   
}

public class PaidByItem {
    int feeAmount = 100;
    int daysPerYear;
    int numberOfMonths;

    public PaidByItem(int numberOfMonths, int daysPerYear) {
        this.numberOfMonths = numberOfMonths;
        this.daysPerYear=daysPerYear;
        System.out.println(this.totalAmount());
    }

    public double totalAmount (){
        return  feeAmount * numberOfMonths / daysPerYear;
    }
}

public class Test {
    public static void main(String args[]){
        GrandParent a = new GrandParent();
    }
}

Note: your formula doesn't take into account percentage rate so I guess it's wrong, and I don't like the original design approach but, I guess, as the names GrandParent, Account are fictitious and I don't really understand the reasons for doing that way, translated it as you have it.

In the test code as son as you instantiate GrandParent, the result is printed.

  • do you mean you don't like the overal design of injecting via constructors? Or how I had laid out my object model in the sample? – Aaron Fischer Aug 27 '16 at 17:57
  • @AaronFischer No. The original design of making the calculation in an object inside an object inside another object. But I guess as the names Aaa, Xyz are fictitious I don't really understand the reasons for doing it that way. – Tulains Córdova Aug 27 '16 at 18:00
  • I see, I am not a fan of having the calculation there. The PaidByItem type does need to hold the total. but I am open to having something else responsible for doing calculations – Aaron Fischer Aug 27 '16 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.