简体   繁体   中英

How to give a child class a modified inherited method?

I'm facing a trouble finding the best OOP design solution to the following problem: I have the parent abstract class Classifier which includes the abstract method classify(Instances dataset) . Two classes extends Classifier , namely NormalClassifier and ThresholdClassifier . Both the children classes implements the classify(Instances dataset) method. However, ThresholdClassifier requires the classify(Instances dataset) method to be of this form classify(Instances dataset, double threshold) and can't never work with classify(Instances dataset) .

Note that ThresholdClassifier can't accept the threshold parameter in its constructor and it must have the parameters in the classify method.

In this case the child class ThresholdClassifier share the same characteristic as the parent but it requires a different method signature. I'm not able to extend the parent class because it would require me to implement the original method signature nor it makes sense not to extend the parent class because it's clearly a Classifier . So how would one solve such a design problem in Java OOP? Is there a technical term for this issue?

Edit 1:

what I basically did to solve this issue is that I created an interface called ThresholdBasedClassifier that contains the method setThreshold(double threshold) . Then I made ThresholdClassifier implement this method and created an internal field called threshold . However, I find this to be an ugly design solution for many reasons, especially that the user could forget that he needs to set change or set the threshold before calling classify(Instances dataset)

Edit 2:

Also the requirements says that there can't be a default value for the threshold .

Edit 3:

My example above was just an example to a common design problem I'm facing in general. So I'm looking for a general solution not a specific solution.

I can see multiple solutions:

  1. Make ThresholdClassifier extends from NormalClassifier .

  2. Change Classifier to a concrete class (remove abstract).

  3. Implement classify(Instances dataset) on ThresholdClassifier calling the classify(Instances dataset, double threshold) with a default threshold value.

For example:

void classify(Instances dataset) {
    classify(dataset, 10);
}
  1. Make two empty methods on Classifier , so each one overrides the apropriate one:

Code:

public abstract class Classifier {
    void classify(Instances dataset) {
    }
    void classify(Instances dataset, double threshold) {
    }
}

public class NormalClassifier extends Classifier {
    void classify(Instances dataset) {
        // Code here
    }
}

public class ThresholdClassifier extends Classifier {
    void classify(Instances dataset, double threshold) {
        // Code here
    }
}

You can even throw an exception inside Classifier methods. This will enforce the implementation on the child class.

As ThresholdClassifier "can't never work with classify(Instances dataset) ", I think this method shouldn't be declared in Classifier abstract class. You should remove classify method from Classifier and declare classify(Instances dataset) in NormalClassifier and classify(Instances dataset, double threshold) in ThresholdClassifier .

ThresholdClassifier should contain an internal value double threshold which allows it to define:

private double threshold = DEF_THRESHOLD;

public void setThreshold(final double threshold){
    this.threshold = threshold;
}

@Override
public void classify(Instances dataset){
    this.classify(dataset, threshold);
}

private void classify(Instances dataset, final double threshold){
    // TODO implement this classify method
}

Additional Info

Are you asking for a general signature? Which I think is worthless because it means that for each different type there will be different interfaces which means you won't get very much reuse out of defining it this way which completely defeats the purpose of using a class hierarchy, interfaces, and abstract classes. Nevertheless there are such methods in Java. You would need something like the following:

public abstract void classify(final Instances dataset, 
    final Object... options) throws IllegalArgumentException

Then your ThresholdClassifier would look something like this:

public ThresholdClassifier extends Classifier{
    @Override
    public void classify(final Instances dataset, 
        final Object... opts) throws IllegalArgumentException {
    if(opts.length != 1)
        throw new IllegalArgumentException(); // probably should say something

    try{
        // casts from Double to double automatically
        final double threshold = (Double)opts[0];
        // TODO implement classify method
        //  ...
    }catch(ClassCastException cce){
        throw new IllegalArgumentException();// should probably say something
    }

You may be able to make the above an abstract class and require a method void classify(final Instances dataset, final double threshold) (I'm not totally sure--it may be considered ambiguous since the double threshold could be interpreted as either a double or a Double ).

You may also consider making the options explicitly be String instead of Object . This at least would allow you to make a general command line driver.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM