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:
Make ThresholdClassifier
extends from NormalClassifier
.
Change Classifier
to a concrete class (remove abstract).
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);
}
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.