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

#java #oop

#java #ооп

Вопрос:

допустим, у меня есть интерфейс, как показано ниже

 public interface ConditionChecker {
    boolean isInCondition(Person p);
}
  

Я хочу создать новый класс, реализующий вышеупомянутый интерфейс, но мне нужно реализовать функцию с другим параметром

 public class MacroConditionChecker implements ConditionChecker {
    public boolean isInCondition(Person p, MacroView mv);
}
  

Две проблемы:
Первый: если я изменю подпись интерфейса на boolean isInCondition(Person p, MacroView mv); , то мне нужно обновить все классы, реализующие ConditionChecker
Второе: я хочу, чтобы потребители ConditionChecker просто вызывали isInCondition как есть

Я думаю, это означает:

 public class MacroConditionChecker implements ConditionChecker {
    private static final MacroView mv;
    public MacroConditionChecker(MacroView mv) {
        this.mv = mv;
    }
    public boolean isInCondition(Person p){
        // now i have access to MacroView
    }
}
  

итак, единственное изменение, которое мне нужно, это внести в то время, когда я решаю использовать MACROC conditionchecker, и вызов isInCondition не изменяется

Я на правильном пути? или есть какой-то другой способ добиться этого?

или я должен выделить MacroView в качестве собственного интерфейса

 public class MacroConditionChecker implements ConditionChecker implements MacroView
  

Комментарии:

1. Это действительно одно из возможных решений. Но тогда он имеет ненужный вид постоянного состояния.. Что, если вы просто создадите новый метод в интерфейсе, который вызывает перегруженный метод с квалификатором по умолчанию?

2. Ваше первое решение довольно хорошее. Но почему вы объявили mv поле как статическое?

3. @YassinHajaj не могли бы вы подробнее рассказать? не на 100% соответствует

4. Вы передаете несколько ConditionChecker классов в функцию, а затем выполняете цикл над ними, вызывая isInCondition , чтобы проверить все условия сразу? Это объясняет ваше желание не менять этот интерфейс. Итак, я думаю, что ваше решение работает хорошо.

Ответ №1:

ConditionChecker напоминает Command шаблон проектирования. Комментарий со связанной страницы:

Команда отделяет объект, который вызывает операцию, от объекта, который знает, как ее выполнить. Для достижения такого разделения разработчик создает абстрактный базовый класс, который сопоставляет приемник (объект) с действием (указатель на функцию-член). Базовый класс содержит метод execute(), который просто вызывает действие на получателе.

Это именно то, что вам нужно. В случае, если вам нужно проверить только внутреннее состояние Person объекта, этого достаточно. Если вы хотите проверить Person объект с помощью external API , то можно создать реализацию, которая связывает external API в конструкторе с Person объектом в методе. Простой пример:

 import java.util.ArrayList;
import java.util.List;

public class DesignPatterns {

    public static void main(String[] args) {
        List<ConditionChecker> checkers = new ArrayList<>();
        checkers.add(person -> person != null);
        checkers.add(person -> person.getName() != null);
        checkers.add(person -> person.getName().length() > 0);
        checkers.add(new MacroViewConditionChecker(new MacroView()));
        checkers.add(new RestApiConditionChecker(new RestApi()));

        Person person = new Person();
        person.setName("Name");

        for (ConditionChecker checker : checkers) {
            System.out.println(checker.isInCondition(person));
        }
    }
}

interface ConditionChecker {

    boolean isInCondition(Person person);
}

class MacroViewConditionChecker implements ConditionChecker {

    private final MacroView macroView;

    public MacroViewConditionChecker(MacroView macroView) {
        this.macroView = macroView;
    }

    @Override
    public boolean isInCondition(Person person) {
        return macroView != null;
    }
}

class MacroView {
}

class RestApiConditionChecker implements ConditionChecker {

    private final RestApi restApi;

    public RestApiConditionChecker(RestApi restApi) {
        this.restApi = restApi;
    }

    @Override
    public boolean isInCondition(Person person) {
        return restApi.checkName(person.getName());
    }
}

class RestApi {

    public boolean checkName(String name) {
        System.out.println("Validate name ...");
        System.out.println(name   " is valid");

        return true;
    }
}

class Person {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  

Вы можете использовать этот шаблон вместе с цепочкой ответственности. Этот подход не привязывает Person объект к какой-либо реализации. Эта привязка выполняется в конкретной ConditionChecker реализации, которую можно легко заменить.

Ответ №2:

Учитывая, что MacroConditionChecker невозможно соблюдать ConditionChecker подпись, тогда какой смысл ее внедрять?

Возможно, лучшим подходом является преобразование MacroConditionChecker class в interface который расширяет ConditionChecker

 interface MacroConditionChecker extends ConditionChecker {
   boolean isInCondition(final Person person, final MacroView macroView);
}
  

А затем предоставьте стандартную / простую реализацию (или что вам нужно)

 class SimpleMacroConditionChecker implements MacroConditionChecker {
   public boolean isInCondition(final Person person, final MacroView macroView) {
      ...
   }
}
  

Те, которым необходимо проверить условие с помощью MacroView , просто примут MacroConditionChecker

 public boolean check(final MacroConditionChecker checker) {
   return checker.isInCondition(this.person, this.macroView);
}
  

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

Комментарии:

1. я не думаю, что это решает вторую проблему, о которой я упоминал. допустим, у меня есть много объектов, использующих ConditionalChecker и вызывающих isInCondition. Если он использует MACROC Conditionchecker, мне теперь нужно пойти и внести изменения во все места, где isInCondition. И если я откатываю MACROC conditionchecker, мне нужно снова внести те же изменения. Я пытаюсь минимизировать, чтобы я мог легко заменить логику для проверки условий

2. @ealeon как я уже сказал, если кому-то нужен дополнительный MacroView для проверки, то он должен принять средство проверки макроусловий. Это наиболее идиоматичный и правильный подход. Так что да, вам придется провести рефакторинг

Ответ №3:

Поскольку интерфейс запрашивает у вас только реализацию данного метода, вы можете перегрузить метод желаемыми параметрами, и соответствующая реализация запустится при передаче дополнительного параметра.

 public class MacroConditionChecker implements ConditionChecker {
    boolean isInCondition(Person p) {};
    public boolean isInCondition(Person p, MacroView mv) {};
}
  

Комментарии:

1. но это не решает вторую проблему, о которой я упоминал, что пользователям ConditionChecker теперь нужно вызывать с дополнительными параметрами. Например, если я решу, что i MACROC conditionchecker больше не используется по умолчанию, мне нужно обойти все места и внести изменения, чтобы он вызывался только с одним аргументом, верно?

2. Java достаточно умна, чтобы вызывать метод, соответствующий классу этого объекта, и использовать реализацию метода, соответствующую параметрам, которые передаются ему во время вызова. Что это сделает, так это просто позволит вам вызывать isInCondition для объектов MACROC Conditionchecker с двумя параметрами в качестве входных данных, это ничего не изменит за пределами класса и не повлияет на другие вызовы, которые используют только ‘Person’ в качестве входных данных.

3. «Что это сделает, так это просто позволит вам вызывать isInCondition для объектов MACROC conditionchecker с двумя параметрами в качестве входных данных» это именно то изменение, которого я пытаюсь избежать. Если у меня есть 1000 объектов, которые теперь хотят использовать MACROC Conditionchecker isInCondition, я не хочу вносить 1000 изменений

4. Это позволило бы вам избежать создания 1000 объектов и настройки MacroView для всех из них во время создания, а вместо этого создать один объект, который имеет дело с любым MacroView, который вы хотите проверить.

5. нет, если я хочу, чтобы MACROC conditionchecker был установлен по умолчанию, и чтобы потребители только подмножества ConditionChecker использовали другой ConditionChecker с одним параметром. дело в том, что мне не нужно вносить какие-либо изменения в потребителей ConditionChecker, нет?