HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Super class knowing the type of its children? Surely there is a better way

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
childrenthesuperwaybettertypeitsknowingthereclass

Problem

I'm working on a small project that does mathematical calculations based on selected data in a JTable. The goal is to make the program fairly adaptable for adding new mathematical calculations in the future. For reference, some of the current calculations are: calculating the mean (average) of a set of data; calculating the percentile of each item in a set of input data; calculating the least squares linear regression (best fit linear function) for a set of data points.

My current design thought process is that to make things the math utilities flexible, there needs to be a some type of base class for analysis data that serves as input and output to some type of performAnalysis function in a base class for all analysis classes. This lead me to a skeleton design as follows:

public enum AnalysisDataType {
    Scalar,
    Set,
    Map
}

public interface IAnalysisData {
    AnalysisDataType getType();
}

public interface IAnalaysisDataScalar extends IAnalysisData {
    double getValue();
}

public interface IAnalaysisDataSet extends IAnalysisData {
    int getCount();
    IAnalysisData getData(int index);
}

public interface IAnalaysisDataMap extends IAnalysisData {
    IAnalysisData getDomain();
    IAnalysisData getCodomain();
}

public interface IDataAnalysis {
    AnalysisDataType getInputDataType();
    AnalysisDataType getOutputDataType();

    IAnalysisData performAnalysis(IAnalysisData inputData);
}


Then, something for calculating mean would be:

```
public class DataAnalysisMean implements IDataAnalysis {
AnalysisDataType getInputDataType() { return AnalysisDataType.Set; }
AnalysisDataType getInputDataType() { return AnalysisDataType.Scalar; }

IAnalysisData performAnalysis(IAnalysisData intputData) {

IAnalysisDataSet inputDataSet = (IAnalysisDataSet)inputData;
double sum = 0;

for (int i = 0; i < inputDataSet.getCount(); i++) {
IAnalysisData dataItem = inputDataSet.getData(i);
IAnalys

Solution

First, I get some error messages when I put this in the compiler.
I had to replace the anonymous inner class that is returned by performAnalysis with a helper class. I'm not a fan of helper classes like this, so it would be nice if someone else had a better idea.

Second, I'm thinking generics would solve this quite nicely.
I agree that the parent class should not need to know it's child classes, but it can obtain this data; see the methods that I have commented out. I have commented them out because I don't like them, but still want to show that it's possible. I'll admit that using .class is a bit tricky when using generics, because of type erasure. However, as long as the generic types are known at compile time, this should not be a problem.

interface IDataAnalysis {

//    public Class getInputDataType( );
//    public Class getOutputDataType( );

    OutputType performAnalysis(InputType inputData);
}

public class DataAnalysisMean implements IDataAnalysis {

//    
//    public Class getInputDataType( ) { return IAnalysisDataSet.class; }
//    public Class getOutputDataType( )  { return IAnalysisDataScalar.class; }

public IAnalysisDataScalar performAnalysis(IAnalysisDataSet inputData) {

    IAnalysisDataSet inputDataSet = (IAnalysisDataSet)inputData;
    double sum = 0;

    for (int i = 0; i < inputDataSet.getCount(); i++) {
        IAnalysisData dataItem = inputDataSet.getData(i);
        IAnalysisDataScalar dataItem2 = (IAnalysisDataScalar)dataItem;
        sum += dataItem2.getValue();
    }

    return new HelperClass( inputDataSet, sum );
}

private class HelperClass implements IAnalysisDataScalar
{
    IAnalysisDataSet inputDataSet;
    double sum = 0;

    HelperClass( IAnalysisDataSet inputSet, double sum )
    {
        this.inputDataSet = inputSet;
        this.sum = sum; 
    }

    @Override
    public double getValue() {
        return sum / inputDataSet.getCount();
    }

    @Override
    public AnalysisDataType getType() {
        return AnalysisDataType.Scalar;
    }
   }
}

Code Snippets

interface IDataAnalysis<InputType extends IAnalysisData, OutputType extends IAnalysisData> {

//    public Class<?> getInputDataType( );
//    public Class<?> getOutputDataType( );

    OutputType performAnalysis(InputType inputData);
}

public class DataAnalysisMean implements IDataAnalysis<IAnalysisDataSet, IAnalysisDataScalar> {

//    
//    public Class<?> getInputDataType( ) { return IAnalysisDataSet.class; }
//    public Class<?> getOutputDataType( )  { return IAnalysisDataScalar.class; }

public IAnalysisDataScalar performAnalysis(IAnalysisDataSet inputData) {

    IAnalysisDataSet inputDataSet = (IAnalysisDataSet)inputData;
    double sum = 0;

    for (int i = 0; i < inputDataSet.getCount(); i++) {
        IAnalysisData dataItem = inputDataSet.getData(i);
        IAnalysisDataScalar dataItem2 = (IAnalysisDataScalar)dataItem;
        sum += dataItem2.getValue();
    }

    return new HelperClass( inputDataSet, sum );
}

private class HelperClass implements IAnalysisDataScalar
{
    IAnalysisDataSet inputDataSet;
    double sum = 0;

    HelperClass( IAnalysisDataSet inputSet, double sum )
    {
        this.inputDataSet = inputSet;
        this.sum = sum; 
    }

    @Override
    public double getValue() {
        return sum / inputDataSet.getCount();
    }

    @Override
    public AnalysisDataType getType() {
        return AnalysisDataType.Scalar;
    }
   }
}

Context

StackExchange Code Review Q#5303, answer score: 2

Revisions (0)

No revisions yet.