Применение принципа разделения интерфейса - PullRequest
0 голосов
/ 24 января 2019

Мне интересно, применяется ли принцип разделения интерфейса к моей кодовой базе.

Вот пример кода:

Первый класс:

public class EntityGroup {
    public List<Entity> tests;

    //returns true if the EntityGroup is valid
    public boolean validate() {
        for (Entity test : tests) {
            if (!test.validateFieldA() || !test.validateFieldB()) {
                return false;
            }
        }
        return true;
    }

}

Второй класс:

public abstract class Entity {

    protected String fieldA;

    public abstract boolean validateFieldA();

    public abstract boolean validateFieldB();

}

Третий класс:

public class EntityChild extends Entity {
    private String fieldB;


    @Override
    public boolean validateFieldA() {
        if (fieldA.equals("valid")) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean validateFieldB() {
        if (fieldB.equals("valid")) {
            return true;
        } else {
            return false;
        }
    }
}

Четвертый класс:

 public class EntityChild2 extends Entity {

    @Override
    public boolean validateFieldA() {
        if (fieldA.equals("valid")) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean validateFieldB() {
        return true;
    }
}

Это очень упрощенный пример из моего реального кода, но я думаю, что он хорошо иллюстрирует проблему,Мой класс EntityChild2 вынужден реализовывать метод, который ему не нужен или о котором он не хочет знать.

Я знаю, что было бы правильнее иметь интерфейс, который содержал бы метод validateFieldB () и имел бы только EntityChild, реализующий этот интерфейс.

С пониманием того, что для рефакторинга этого решения потребуется значительное количество усилий, мне трудно оправдать время, которое потребуется для реализации этого решения в моей реальной кодовой базе.

С какими потенциальными проблемами я могу столкнуться, оставив свой код таким образом?

Какие преимущества я получу от рефакторинга моего кода, чтобы иметь отдельный интерфейс для validateFieldB ()?

tldr: Почему принцип разделения интерфейсов так важен?

1 Ответ

0 голосов
/ 25 января 2019

Неправильная абстракция

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

Из-за общего поведения у вас есть дублированный код в методах validateFieldA из EntityChild и EntityChild2. Метод validateFieldB выглядит очень похоже на validateFieldA, просто поле для проверки равенства является другим.

Вам нужен только один Entity

Шаблон стратегии

При использовании паттерна стратегии у вас не будет повторяющегося кода:

class EqualValidationStategy() implements ValidationStategy<T> {
    @Override
    public boolean check(T a, T b) {
        return a.equals(b)
    }
}

class TrueValidationStategy() implements ValidationStategy<T> {
    @Override
    public boolean check(T a, T b) {
        return true;
    }
}

Entity

public class Entity {
    private String fieldA;
    private String fieldB;
    private ValidationStategy<String> validationForA;
    private ValidationStategy<String> validationForB;

    // all-args consturctor

    @Override
    public boolean validateFieldA() {
        return validationForA.check(fieldA, "valid");
    }

    @Override
    public boolean validateFieldB() {
        return validationForB.check(fieldB, "valid");
    }
}



// Validates fieldA and "ignores" fieldB
Entity example = new Entity(fieldA, 
                            fieldB, 
                            new EqualValidationStategy(), 
                            new TrueValidationStategy());
...