Reuse conditional statements in sub-classes.

1.4k views Asked by At

I have below classes:

public abstract class Parent {

    public abstract boolean checkName(String str);
}


public class Child1 extends Parent {

    public static final String NAME = "CHILD1";

    @Override
    public boolean checkName(String str) {
        //check input validity:
        if (!NAME.equals(str)) {
            throw new IllegalArgumentException("some thing");
        }
        //...
    }
}


public class Child2 extends Parent {

    public static final String NAME = "CHILD2";

    @Override
    public boolean checkName(String str) {
        //check input validity:
        if (!NAME.equals(str)) {
            throw new IllegalArgumentException("some thing");
        }
        // ...
    }

}

You can see check input validity parts of checkName methods in both classes are same.I know there is no way to move this joint part to abstract checkName method of parent,but is there a way to avoid this repeating?

5

There are 5 answers

1
David On BEST ANSWER

You might switch things around so that the checkName() function is concrete in the Parent class, have it do the input validity check, then invoke some abstract method that your child classes implement to do the rest of the processing.

1
DJClayworth On

Yes. On the parent add:

protected void checkNameEquality(String str) {
          //check input validity:
    if (!getNameForClass().equals(str)) {
        throw new IllegalArgumentException("some thing");
    }

}

and on each child call

checkNameEquality(str);

To access the static name you need to add another abstract method to get the name for that class.

abstract String getnameForClass();

and implement it in each child.

There is no way to FORCE an implementor to make that check, but you only have to write the code once.

1
Alex On

Do it in regular way. Create real method "check input validity" in abstract class and use it within any method of your real classes. That's the regular way and it's pretty often used for refactoring.

Also having one static variable for all classes has no sense. Here is what, I think, you need:

public abstract class Parent {

    public String NAME;

    public abstract boolean checkName(String str);

    public void checkInputValidity(String str) {
        if (!NAME.equals(str)) {
            throw new IllegalArgumentException("some thing");
        }
    }
}

class Child1 extends Parent {
    public Child1() {
        NAME = "CHILD1";
    }

    @Override
    public boolean checkName(String str) {
        checkInputValidity(str);
        // ...
        return true;
    }
}

class Child2 extends Parent {

    public Child2() {
        NAME = "CHILD2";
    }

    @Override
    public boolean checkName(String str) {
        // check input validity:
        checkInputValidity(str);
        // ...
        return true;
    }

}
3
6ton On

Create a method in your abstract class with the common implementation and call the custom (overridden) implementation from there:

public abstract class Parent {
    public final boolean checkName(String str) {
        if (!getName().equals(str)) {
            throw new IllegalArgumentException("some thing");
        }

        return _checkName(str);
    }

    public abstract boolean _checkName(String str);

    public abstract String getName();
}


public class Child1 extends Parent {

    public static final String NAME = "CHILD1";

    @Override
    public boolean _checkName(String str) {
        //...
    }

    public String getName() {
        return NAME;
    }
}


public class Child2 extends Parent {

    public static final String NAME = "CHILD2";

    @Override
    public boolean _checkName(String str) {
        // ...
    }

    public String getName() {
        return NAME;
    }

}

Alternatively, you can also call parent's checkName from the child methods.

0
Chetan Kinger On

David's answer covers the crux of the solution but does not cover intricacies of the solution. The other answers (such as the one from Alex) also cover some details of the solution but are limited to checking the name as part of the condition. What would be ideal is to have a generalized solution that can work for any String field not just for checking the name.

There are two parts of your code that you need to abstract out :

  1. The condition that must be satisfied inorder to perform an operation
  2. The operation to perform when the condition is satisfied

This is what you can do :

public abstract class Parent {

   public final void doSomething(String str) {
        if(getCondition(str)) {
            doSomethingOnCondition(str);
        } else {
            throw new IllegalArgumentException("some thing");
        }
    }

    protected abstract boolean getCondition(String str);

    protected abstract void doSomethingOnCondition(String str);


}


class Child1 extends Parent {

    public static final String NAME = "CHILD1";

    @Override
    protected void doSomethingOnCondition(String str) {
        System.out.println("doing something with "+str);
    }

    @Override
    protected boolean getCondition(String str) {
        return NAME.equals(str);
    }
}

Client code can then simply call the doSomething method as follows :

parent.doSomething(str);

A couple of points to note :

  1. We make doSomething final because that's the piece of code we don't want the subclasses to change. We make it public so that it is visible to client code.
  2. We make getCondtion and doSomethingOnCondition protected so that they are visible only to the child classes and not the client code. We make them abstract so that the child classes can assemble the condition and the work to be done after the condition is satisfied.
  3. With generics, you can extend your solution to work for any data-type rather than just String. All you have to do is introduce a type-parameter in Parent