Reputation: 2243
The program I'm helping to develop is supposed to output several dynamically generated questions for the user to answer. The questions are of different types and have a corresponding class Constraint
that they fill with the user-given information. My question is how to create uniform behavior for the different constraints.
---->Constraint<--------------------
| | |
FConstraint PConstraint TConstraint
| |
UConstraint AConstraint
The base class Constraint
is empty, as is TConstraint.
UConstraint
, PConstraint
and AConstraint
share three variables. However, UConstraint
and AConstraint
have one additional variable that PConstraint
doesn't have.
I feel like I'm trying to hammer through a brick wall with some pincers. My one thought is to provide an abstract method to Constraint with the signature:
// All answers are of type string.
abstract void setValue(string variable, string answer);
Which are implemented by every Constraint
subclass. However, passing a string to determine which variable to set seems error-prone and a similarly bad code smell.
The second option was to move the three similar variables up into Constraint, but that still leaves UConstraint, AConstraint
with that extra bit of information I might need to set. It doesn't help that TConstraint
doesn't need any of those.
My current brute-force "Screw this design." solution is an instanceof
soup in which I check and fill in the missing Constraint-specific information.
Object constraint = item.getConstraint();
if (constraint instanceof AConstraint) {
AConstraint constraint = (AConstraint) constraint;
if (constraint.getValue() == null) {
constraint.setValue(string);
} else if (constraint.getKey() == null) {
constraint.setKey(string);
} // More of the same.
} else if (constraint instanceof PConstraint) {
// As the above if() group.
} // etc.
Is there a better solution to this design than the abstract function?
Upvotes: 4
Views: 223
Reputation: 31795
Your question don't have enough information about actual work you need to do in each case, but generally such code:
Object constraint = item.getConstraint();
if (constraint instanceof AConstraint) {
// Work
} else if (constraint instanceof PConstraint) {
// Work
} // etc.
is a strong smell to use polymorphism and refactor into a something like this:
Constraint constraint = item.getConstraint();
constraint.doWork(...);
where specific classes would look something like this:
public class AConstraint {
public ... doWork(...) {
if (getValue() == null) {
setValue(string);
} else if (getKey() == null) {
setKey(string);
} // More of the same.
}
}
Upvotes: 3
Reputation: 33534
Use this principle
Program in interfaces, and encapsulate the behaviors that keeps changing in abstract class or Interfaces.
eg: For your above given example
Interface - Constraint
Abstract Class - FConstraint
Concrete Class - PConstraint, TConstraint, UConstraint, AConstraint
Upvotes: 3
Reputation: 1866
Have the Constraint
as an Interface. Define an abstract class which extends this Constraint
and this should have the shared variables. UConstraint
, AConstraint
should extend this abstract class which has the shared variables. The rest of the classes can directly implement the Constraint
interface. The instance of code should be changed like
Constraint constraint = item.getConstraint();
constraint.doWork(..);
Always the better design is to write code against an interface
Upvotes: 1
Reputation: 61510
You could do something like this:
public interface Constraint{}
public abstract class VariableConstraint implements Constraint
{
/* hold 3 shared variables here */
}
public class UConstraint extends VariableConstraint{}
public class PConstraint extends VariableConstraint{}
public class AConstraint extends VariableConstraint{}
public abstract class EmptyConstraint implements Constraint {}
public class TConstraint extends EmptyConstraint {}
Upvotes: 2
Reputation: 28762
Specify common functionality in Constraint
: any operation that you want to be able to perform on a Constraint
object (which could in fact be an object of one of the sub types), you have a method for that functionality in Constraint
that you override in subclasses.
You can have trivial (e.g. empty) implementation for the parent classes where that functionality does not make sense.
This way you do not need to concern yourself with the concrete class of the object, just use the facility provided by the superclass
Upvotes: 1