Dave L
Dave L

Reputation: 3515

What Is the Proper Way to Handle Calculated Properties in an Object?

From what I can tell, there are two main ways to accomplish this:

I created two examples using a simple insurance policy object. Each class initializes with a few properties like premiumRateBase, brokerFee, termYears, effectiveDate, and agentCommissionRate. The calculated columns would be things like the prorated premiumRate, total, or agentCommission.

Here's an example of option 1:

component {

    // public properties (getters + setters)
    property name="premiumRateBase";
    property name="brokerFee";
    property name="effectiveDate";
    property name="termYears";
    property name="agentCommissionRate";

    function init(
        required numeric premiumRateBase,
        required numeric brokerFee,
        required date effectiveDate,
        required numeric termYears,
        required numeric agentCommissionRate
    ) {

        // setters
        ...

        return this;

    }

    function getExpirationDate() {
        return dateAdd( 'yyyy', effectiveDate, termYears );
    }


    function getPremiumRate() {

        // run proration and calcuation determination 
        // based on premiumRateBase, getExpirationDate(), and maybe a few other methods
        ...

        return premiumRate;

    }


    function getTotal() {
        return getPremiumRate() + brokerFee;
    }


    function getAgentCommission() {
        return getPremiumRate() * agentCommissionRate
    }


    function getAgentCompensation() {
        return getAgentCommission() + brokerFee
    }

}

In the above example, the calculations are run any time you call a method like getTotal(). The advantage of this approach is that the code is pretty straightforward. The disadvantage of this approach is that if needed to run getTotal(), getAgentCommission() and then getAgentCompensation(), you wind up running a lot of redundant math. For this example, it wouldn't equate to much additional processing time, but in a more complex example, I could see this adding up.

Here's an example of option 2:

component {

    // public properties (getters + setters)
    property name="premiumRateBase";
    property name="brokerFee";
    property name="effectiveDate";
    property name="termYears";
    property name="agentCommissionRate";


    function init(
        required numeric premiumRateBase,
        required numeric brokerFee,
        required date effectiveDate,
        required numeric termYears,
        required numeric agentCommissionRate
    ) {

        // setters
        ...

        // run the calculation
        calculate();

        return this;

    }


    // primary calculation method which sets all private properties
    function calculate() {

        variables.expirationDate = calculateExpirationDate();
        variables.premiumRate = calculatePremiumRate();
        variables.total = calculateTotal();
        variables.agentCommission = calculateAgentCommission();

    }


    /***************************
        Public Getters
    ***************************/

    function getExpirationDate() {
        return expirationDate;
    }

    function getPremiumRate() {
        return premiumRate;
    }

    function getTotal() {
        return total;
    }

    function getAgentCommission() {
        return agentCommission;
    }


    /***************************
        Private Calculations
    ***************************/

    private function calculateExpirationDate() {
        return dateAdd( 'yyyy', effectiveDate, termYears );
    }


    private function calculatePremiumRate() {

        // run proration and calcuation determination 
        // based on premiumRateBase, expirationDate and maybe a few other variables
        ...

        return premiumRate;

    }


    private function calculateTotal() {
        return premiumRate + brokerFee;
    }


    private function calculateAgentCommission() {
        return premiumRate * agentCommissionRate;
    }


    private function calculateAgentCompensation() {
        return agentCommission + brokerFee;
    }


}

In the second example, we only run a generic calculate() method after the constructor method init() fires. I didn't include this, but you would also need to run calculate() again if you ever update any of the public properties through their setter methods. The advantage of this approach is that the calculation math only occurs when the properties change. The downside is that the code seems a little more convoluted and harder to read.

What is the best-practices or proper approach to solving this type of problem?

Upvotes: 1

Views: 161

Answers (1)

Vishal Shukla
Vishal Shukla

Reputation: 2998

It is a common dilemma and ultimately it resolves to the cost of recalculating the property every time. If it is cheap, I would always prefer getter approach.

Another dimension to think about is staleness of calculated property. Option #2 performs calculation only upon initialisation and possibly the properties involved in calculation may change afterwards. The calculated metric will be stale now. You can fix it by recalculating metrics on modification. This will further increase the complexity of code. If this modification isn't well encapsulated, recalculation responsibility will be shared with caller too!

In summary, for cheap calculations I would prefer option #1 and for complex calculations, I would first encapsulate modifications to ensure recalculation on each update.

Upvotes: 2

Related Questions