# Redesign a module to be generic

I am having trouble designing a module, can anybody help me?

Because it will be hard to maintain this kind of module, I also think that this can test my skill of design pattern usage.

# Requirement

This is basically an agricultural project (web application). I need to design a module where some calculation takes place.

There are different crops involved like maize, tomato, okra etc. Each of these crops has different traits.

Each trait has a measurement scale which lies in integer like 200-1000. Now let's say I have planted the crop and done measurement noted down the traits. Now I want to do some sort of measurement. Some measurements are simple and some are complex.

# Example

Lets take an example of crop maize. I have recorded observations for 15 traits. (We'll use trait1-trait15 as examples, the actual name can be like plt_ht, yld, etc.)

I recorded 5 observations for each trait:

trait1 trait2 trait3 trait5 trait6..... trait15
01,02,03,04 01,02,03,04 01,02,03,04

User logs into system and selects his crops and enters data for these observations. I have to calculate either average or sum of the data entered for each trait.

# Complexity / centre of the problem

So far it's simple but complexity comes when I have some different formulas for some of the traits.

Example: trait YLD has a formula based on which I have to calculate its value, which may also depend on some other traits. Each different crop can have different traits.

All this I am able to do - whenever user selects crop I will check for those specific traits and do calculations (if it's not a special trait then I either average or sum it, based on db entry), but there is a lot of hard coding.
I would like to have suggestions on a better way of handling this.

My code needs to handle both simple and complex calculations.
Simple calculations are easy, I have take average of value entered for trait.
The problem comes when I have to do complex calculations, since each crop have different traits with their own formulas, so to calculate I have to check for crop and then for complex trait. So I have to hardcode the trait name of complex traits.
Can any tell me how I can design this using Java oops [?!?] so that I can make it generic?

I have about 10 different crops. Some calculations are specific to crops, so there will be lot of code like the if below:

    hasZeroValue = (HashMap<String, ArrayList<String>>) dataValues[1];
} else if(cropId.equalsIgnoreCase("MZ") && traitName.equalsIgnoreCase("Shelling")) {
avg=HybridTestDataUtility.calculateAvg(traitName, dataPoint, dataTraits, traitValues,dataPvalues, dataPoint, type);
avg=avg*dataPoint;
traitAvg=getMaizeYeild(traitName, traitAvg, population, avg, hybrid, area);
} else if(cropId.equalsIgnoreCase("OK") && traitName.equalsIgnoreCase("YLDGM")) {
avg=HybridTestDataUtility.calculateAvg(traitName, dataPoint, dataTraits, traitValues,dataPvalues, dataPoint, type);
//avg=avg*dataPoint;
Object[] dataValues=getOKRAYield(traitName, traitAvg, population, avg, dividend,hasZeroValue,hybrid,repl);
traitAvg = (HashMap<String, Float>) dataValues[0];
hasZeroValue = (HashMap<String, ArrayList<String>>) dataValues[1];
} else if(cropId.equalsIgnoreCase("HP") && traitName.equalsIgnoreCase("w1-w10")) {
avg=HybridTestDataUtility.calculateAvg(traitName, dataPts, dataTraits, traitValues,dataPvalues, dataPoint, type);
avg=avg*dataPoint;
Object[] dataValues=getHotPepperYield(traitName, traitAvg, population, avg,dividend,hasZeroValue,hybrid,repl);
traitAvg = (HashMap<String, Float>) dataValues[0];
hasZeroValue = (HashMap<String, ArrayList<String>>) dataValues[1];
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("TLSSG_70")) {
traitAvg=calculateTLCV(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues,50);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("TLSSG_100")) {
traitAvg=calculateTLCV(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues,50);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("YVMV_60")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("YVMV_90")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("YVMV_120")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("ELCV_60")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("ELCV_90")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("TO") && traitName.equalsIgnoreCase("ELCV_120")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("OK") && traitName.equalsIgnoreCase("YVMV_60")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("OK") && traitName.equalsIgnoreCase("YVMV_90")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg,dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("OK") && traitName.equalsIgnoreCase("YVMV_120")) {
traitAvg=tomatoYVMVCalculation(traitName, traitAvg, dataPoint, dataTraits, hybrid, repl, traitValues, dataPvalues);
} else if(cropId.equalsIgnoreCase("OK") && traitName.equalsIgnoreCase("ELCV_60")) {


Can anybody think of a way to make a generic approach to this?

TO Make things more understandable

There are crops and each crop have traits , traits are actually a mesuremet scale to decide growth of a seed of a particular crop. This module is to for planters to observe growth of seeds sowed of certain crops and take down n no of observation for each trait and upload in csv format.Once they enter data i have to either avg out the values or sum the values or sometimes there are more complex function that i have to apply it may differe for each trait .This is the whole module about.Just to give an idea about how they will enter data

Hyubrid(seed) trait1 trait2 trait3 trait5 trait6..... trait15
Hybrid1 01 02 03 04 01
HYbrid2 04 06 08 04 01
HYbrid2 04 06 08 04 01
HYbrid2 04 06 08 04 01
HYbrid2 04 06 08 04 01


Once they enter data in this format i have to give result something like this.

Here avg colum does not necessaryly mean avg it can be sum or any formula based resutl.Hybrid is the seed for which they record the observation. I have shown avg column only for two tratis it is actually for all the traits.

Hyubrid(seed) trait1 Avg trait2 avg trait3 trait5 trait6..... trait15
Hybrid1 01 01 02 04 03 04 01
HYbrid2 04 04 06 10 08 04 01
HYbrid2 04 04 06 12 08 04 01
HYbrid2 04 04 06 14 08 04 01
HYbrid2 04 04 06 12 08 04 01


Hope this clarifies atleat a but

The data are not correctly indented but there is no way i can format it.

-
Next time please make an effort when asking. You can as a bare minimum avoid spelling mistakes by using your browser's spelling tools. I am exhausted after this edit... –  ANeves Dec 5 '12 at 11:40

I'm not entirely sure of what you're asking, but try this:

• Create a Crop class
• Crop has a list of Traits
• Create a Trait class
• Trait has an Enum to define which calculation it uses (for example: Calculations.Average, Calculations.TLCV, Calculations.TomatoTVMV, and so on)
• Trait has a list of Observations
• Trait has a Calculate() function, which processes all Observations according to the specified calculation.

You'll still need to code each calculation function, but you don't need to manually attach each calculation to a specific crop or trait. You can just have a database table which tells the code "Crop + Trait = Enum value"

-
Thanks for the edit ANevesless –  VIckyb Dec 7 '12 at 5:26
I have implemented it it my own way but what i thought this module could be case study where developers can think from extensebility maintainbility point of view so any design pattern suggestion will be helpfull –  VIckyb Dec 7 '12 at 5:28
Just added some more things to clarify –  VIckyb Dec 13 '12 at 7:42
There's no limit on the number of values in an Enum, but with that many options you're going to need to centralize all the logic which changes based on the value, so you only have to maintain it in one place. –  Bobson Jan 16 '13 at 14:25
Create a single function: RunCalculations(). Inside that is a giant switch statement which does the right calculation for your Enum value. Everything which needs a calculation calls that one function. Thus, if you modify the possible values, you only have one place to change it. –  Bobson Jan 17 '13 at 14:30