有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在这里如何同时遵守“组合优先于继承”和DRY原则?

认为有一个简单的用例,父类具有子类,这些类具有共同的属性,例如:类动物,其名称为:

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

    abstract void printInfo();
}

和子类:

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");
    }
}

根据Prefer composition over inheritance?https://softwareengineering.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables,应该避免继承和保护变量,因此我将Animal修改为一个接口:

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

但是当移动类属性时,噩梦就来了:

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");
    }
}

其中包含以下代码:

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

需要复制并粘贴到每个类中。此外,如果还有一个属性需要添加,例如:weight,我需要手动更新Animal和每个子类

我的问题是,这是否违反了干燥原则?如果是这样的话,有没有方法重构原始代码以避免继承和受保护的变量,同时也遵守DRY原则,这样我就不需要复制和粘贴关于公共属性的代码到每个子类中

(或者原版已经可以了?)


共 (3) 个答案

  1. # 1 楼答案

    作为变量,为了避免在Cat/Dog类中复制粘贴'name'变量,您可以使用单独的类来处理名称。例如:

    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.");
        }
    }
    
  2. # 2 楼答案

    Prefer composition over inheritance

    在你的第二种方式中,你不使用构图
    在子类中实现所有抽象方法。这是不同的
    这里您遇到了一个复制问题,因为您没有一个抽象的骨架类作为所有具体类的基类
    这确实是一件截然不同的事情

    事实上,“偏好组合而非继承”仅适用于非为继承而设计的类
    抽象类是为继承而设计的:它是抽象的,并且有一个抽象方法

    您使用继承的第一种方式是有意义的。 因此,在这种情况下,良好的实践是使用继承,它也允许尊重干式原则

    对于不是为继承而设计的类,应该支持组合,在这种情况下,您必须在composer类中包装/组合该类

  3. # 3 楼答案

    inheritance and protected variables should be avoided

    继承是可以的,只要您不强迫用户在他们需要一个接口时使用它。Java的List<T>AbstractList<T>提供了一个很好的例子:如果需要使用部分共享实现,那么继承抽象类;如果没有,请实现接口

    protected String name字段也可以设置为private,从而消除了受保护变量的使用

    以下是该方法如何适用于您的类层次结构:

    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");
        }
    }