2

Consider there is a simple use case, a parent class with child classes which has common properties, eg: class Animal which has a name:

public class abstract Animal{
    protected String name;
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }

    abstract void printInfo();
}

and subclass :

public class Cat extends Animal{
    @Override
    public void printInfo(){
        System.out.println("I'm a cat");
    }
}

public class Dog extends Animal{
    @Override
    public void printInfo(){
        System.out.println("I'm a dog");
    }
}

according to Prefer composition over inheritance? and https://softwareengineering.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables, inheritance and protected variables should be avoid, so I modified Animal into an interface:

public interface Animal{
    void setName(String name);
    String getName();
    void printInfo();
}

but the nightmare comes when moving the class property:

public class Cat implements Animal{
    private String name;
    @Override
    public void setName(String name){
        this.name=name;
    }
    @Override
    public String getName(){
        return name;
    }
    @Override
    public void printInfo(){
        System.out.println("I'm a cat");
    }
}

public class Dog implements Animal{
    private String name;
    @Override
    public void setName(String name){
        this.name=name;
    }
    @Override
    public String getName(){
        return name;
    }
    @Override
    public void printInfo(){
        System.out.println("I'm a dog");
    }
}

which the following code:

private String name;
@Override
public void setName(String name){
    this.name=name;
}
@Override
public String getName(){
    return name;
}

needs to copy and paste into each class. Furthermore, if there is one more property to add, eg:weight, I need to update Animal and each subclass manually.

My question is, is it violating DRY principle? if so, is there any method to refactor the original code so that it avoids inheritance and protected variables and also obeys DRY principle at the same time, so that I don't need to copy and paste codes about common properties into each subclass?

(or is the original already fine?)

ocomfd
  • 4,010
  • 2
  • 10
  • 19
  • 1
    If you read carefully the links that you posted, you'll see that none of them say to do one thing 100% of the time. Especially relevant is the answer in the question in your first link, where it says: `Does TypeB want to expose the complete interface (all public methods no less) of TypeA such that TypeB can be used where TypeA is expected? Indicates Inheritance`. In this case you're definitely exposing the whole interface, but other than that your first version of the code looks cleaner and more readable to me. – ChatterOne Feb 01 '18 at 09:14

3 Answers3

5

inheritance and protected variables should be avoided

Inheritance is fine, as long as you are not forcing users into using it when all they need an interface. Java's List<T> and AbstractList<T> provide a good example: if you need to use parts of shared implementation, inherit the abstract class; if you don't, implement the interface.

protected String name field could be made private, too, eliminating the use of protected variables.

Here is how the approach would work for your class hierarchy:

public interface Animal {
    void setName(String name);
    String getName();
    void printInfo();
}

public abstract class AbstractAnimal implements Animal {
    private String name;
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    abstract void printInfo();
}

public class Cat extends AbstractAnimal {
    @Override
    public void printInfo(){
        System.out.println("I'm a cat");
    }
}

public class Dog extends AbstractAnimal {
    @Override
    public void printInfo(){
        System.out.println("I'm a dog");
    }
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • is there any way to deal with the protected property "name" if the subclass needs to access it at printInfo()? – ocomfd Feb 01 '18 at 09:35
  • 1
    @Ipad1gs Sure - `getName()` makes `name` available to `printInfo` and other code. JIT compiler makes accessing it as efficient as if it were a protected field, so you don't lose any efficiency either. – Sergey Kalinichenko Feb 01 '18 at 09:39
2

As variant to avoid copy-paste of 'name' variable in Cat/Dog classes you can use separate class for name handling. Example:

public interface Animal {
    void setName(String name);
    String getName();
    void printInfo();
}

public class DefaultNameHolder {
    private String name;
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
}

// Same for Dog class
public class Cat implements Animal {
    private DefaultNameHolder nameHolder = new DefaultNameHolder();

    @Override
    public void setName(String name) {
        // Delegation
        nameHolder.setName(name);
    }

    @Override
    public String getName() {
        // Delegation
        return nameHolder.getName();
    }

    @Override
    public void printInfo(){
        System.out.println("I'm a cat");
    }
}

public class Developer implements Animal {
    private String firstName;
    private String lastName;

    @Override
    public void setName(String name) {
        // Some special logic for setName() 
        this.firstName = name.split(" ")[0];
        this.lastName = name.split(" ")[1];
    }

    @Override
    public String getName() {
        // Some special logic for getName()
        return "My first name is " + firstName + ", my last name is " + lastName;
    }

    @Override
    public void printInfo(){
        System.out.println("Will code for food.");
    }
}
0

Prefer composition over inheritance

In your second way, you don't use composition.
You implement all abstract methods in the subclasses. Which is different.
Here you fall on a duplication problem as you don't have a abstract skeleton class as base class for all concrete classes.
It is really a distinct matter.

In fact "Prefer composition over inheritance" is right only for classes not designed for inheritance.
Your abstract class is designed for inheritance : it is abstract and has an abstract method.

Your first way using the inheritance makes sense. So in this case, good practice is using inheritance and it allows also to respect the DRY principle.

For classes not designed for inheritance, composition should be favored and in this case, you have to wrap/compose the class in the composer class.

davidxxx
  • 125,838
  • 23
  • 214
  • 215