Можно ли избежать дублирования кода при реализации методов для двух похожих классов? - PullRequest
0 голосов
/ 06 мая 2018

У меня есть два класса: Fish и Plant. Они не наследуются ни от каких классов.

Но оба они имеют один метод с именем isAlive(), который имеет одинаковые детали реализации. Теперь у меня есть список рыб и еще один список собак, и мне нужно удалить мертвую рыбу и мертвую собаку. Я хочу, чтобы у моего метода было то же имя, но это невозможно без добавления дополнительного поля в сигнатуру метода. Возможно ли, что мне не нужно писать дополнительный фрагмент кода, который делает то же самое, что и последний фрагмент кода?

Ниже приведен код. Для класса Model, Fish и Plant - это два элемента данных, и они представляют собой ArrayList объектов Fish и Plant.

Можно ли как-нибудь написать только один метод с именем count, и мне не нужно добавлять дополнительное поле в сигнатуру моего метода или изменять тип возвращаемого значения?

public class Fish{
    public boolean isAlive(){
        if(this.size > 0){
            return true;
        }
        return false;
    }
}
public class Plant{
    public boolean isAlive(){
        if(this.size > 0){
            return true;
        }
        return false;
    }
}

public class Model{
    private int countDeadFish() {
        int totalCount = 0;
        for(Fish aFish : this.fish) {
            if(aFish.isAlive() == false) {
                totalCount += 1;
            }
        }
        return totalCount;
    }

    private int countDeadPlants() {
        int totalCount = 0;
        for(Plant plant : this.plants) {
            if(plant.isAlive() == false) {
                totalCount += 1;
            }
        }
        return totalCount;
    }
}

Ответы [ 5 ]

0 голосов
/ 06 мая 2018

Судя по комментариям, вы не можете изменить Fish или Plant. Вот подход к уменьшению дублирования в countDead<Something> методах, который не требует этого.

В основном вы хотите считать элементы в массиве, которые удовлетворяют определенным критериям. В Java 8 вы можете зафиксировать этот критерий в предикате, используя лямбда-выражения или ссылки на методы. Для этого вам не нужно наследование или реализация определенного интерфейса.

private long countDeadFish() {
    return countDeadItems(this.fish, Fish::isAlive);
}

private long countDeadPlants() {
    return countDeadItems(this.plants, Plant::isAlive);
}

private <T> long countDeadItems(Collection<T> items, Predicate<? super T> isAlive) {
    return items.stream().filter(isAlive.negate()).count();
}
0 голосов
/ 06 мая 2018

Использовать интерфейс

public interface LiveObject {
    boolean isAlive();
}

public class Fish implements LiveObject {
    public boolean isAlive(){
        if(this.size > 0){
            return true;
        }
        return false;
    }
}

public class Plant implements LiveObject {
    public boolean isAlive(){
        if(this.size > 0){
            return true;
        }
        return false;
    }
}

public class Model{

    private int countDead(Collection<LiveObject> objects) {
        int totalCount = 0;
        for(LiveObject obj : objects) {
            if(obj.isAlive() == false) {
                totalCount += 1;
            }
        }
        return totalCount;
    }

    private int countDeadFish() {
        return countDead(this.fish);
    }
}
0 голосов
/ 06 мая 2018

Вы можете создать служебный метод (в утилите class где-нибудь):

public final class Liveliness {

    private Liveliness() {
    }

    public static boolean isAlive(final IntSupplier sizer) {
        return sizer.getAsInt() > 0;
    }

}

Ваш метод становится:

public boolean isAlive(){
    return Liveliness.isAlive(this::getSize);
}

В качестве альтернативы используйте interface Life:

public interface Life {

    int getSize();

    default boolean isAlive(){
        return getSize() > 0;
    }
}

Таким образом, добавление метода getSize и наследование от Life добавят метод.


Обратите внимание, избегайте следующих антипаттернов:

if(test) {
    return true;
} else {
    return false;
}

Использование return test.

0 голосов
/ 06 мая 2018

Поскольку Fish и Plant не наследуют ни от чего, вы можете рассмотреть вопрос о создании суперкласса и расширить его:

public class LivingThing {
    protected int size = 1;
    public boolean isAlive() {
        return size > 0;
    }
}

public class Plant extends LivingThing {
}

public class Fish extends LivingThing {  
}

В этом примере используется наследование для классификации Plant и Fish в суперкласс LivingThing. Вы можете установить size, например, в конструкторе Plant или в методе экземпляра:

public class Plant extends LivingThing {
    public Plant(int size){
        this.size = size;
    }
}

Ваш Model может быть:

public class Model{
    private int countDeadFish() {
        return countDead(this.fish);
    }

    private int countDeadPlants() {
        return countDead(this.plants);
    }

    private int countDead(ArrayList<LivingThing> things) {
        int totalCount = 0;
        for(LivingThing thing: things) {
            if(!thing.isAlive()) {
                totalCount++;
            }
        }
        return totalCount;
    }
}
0 голосов
/ 06 мая 2018

Если вы не хотите использовать наследование, тогда вы можете использовать общий метод:

public class AliveChecker {

    public static boolean isAlive(int size) {
        return size > 0;
    }

}

public class Plant{
    public boolean isAlive(){
        return AliveChecker.isAlive(this.size);
    }
}

public class Fish{
    public boolean isAlive(){
        return AliveChecker.isAlive(this.size);
    }
}
...