Говорит ли Принцип Открытости/Закрытости, что мы не можем менять интерфейсы наших классов?

Посмотрите на этот класс, пожалуйста:

public class SomeClass {

public void method1(){
    // do something
}

public void method2(){
    // do something
}

}

Далее представьте, что 10 классов наследуют этот класс. Итак, если мы добавим параметр к методу1(), например, мы должны изменить 10 классов. Это противоречит принципу Open/Closed. Итак, говорит ли Принцип Открытости/Закрытости, что мы не можем изменить публичный интерфейс наших классов?


person Александр Елиза&    schedule 03.04.2016    source источник
comment
Вам предстоит сменить более 10 классов. Вы должны изменить каждый класс, который будет вызывать method1(), потому что ни один из этих вызовов с 0 аргументами больше недействителен. Я бы не стал так беспокоиться об OCP, как о простой обратной совместимости. Конечно, если вы контролируете весь вызывающий код, это нормально. Это действительно зависит от контекста.   -  person Jon Skeet    schedule 03.04.2016
comment
programmers.stackexchange.com/questions/310603/   -  person Anmol Gupta    schedule 03.04.2016
comment
@JonSkeet, так что, если я контролирую весь вызывающий код, я могу изменить общедоступный интерфейс? Если я не контролирую, я должен сохранить старую версию метода и добавить новую версию? После этого я могу объявить старую версию устаревшей?   -  person Александр Елиза&    schedule 03.04.2016


Ответы (2)


Да, вроде... #8>P

Бит "Закрыто для изменений" говорит, что вы не должны ни удалять подпись public void method1(), ни изменять ее семантику (должна быть предоставлена ​​всеми правильно реализующими конкретные классы).

Бит "Открыть для расширения" позволяет (осторожно) изменять интерфейс, пока предыдущие элементы интерфейса остаются как есть (структурно и семантически). Вы можете попробовать что-то вроде этого:

public class SomeClass {

    @Deprecated
    default public void method1(){
        method1(#SENTINAL#);
    }

    public void method1(#TYPE# arg){
        if (arg == #SENTINAL#) {
            // do something old
        } else {
            // do something new
        }
    }

    public void method2(){
        // do something
    }

}

ПРИМЕЧАНИЕ. Этот подход может не работать, если аргумент не имеет полезного значения #SENTINAL#, например. это int и все значения допустимы. Однако часто есть что-то, например, любое отрицательное значение, значение MIN или MAX, пустой или нулевой объект и т. д. Когда такой контрольный параметр недоступен, вы можете создать частный общий метод, например:

public class SomeClass {

    @Deprecated
    default public void method1(){
        method1(true);
    }

    public void method1(#TYPE# arg){
        method1(false, arg)
    }

    private void method1(boolean isOldWay, #TYPE# arg) {
        if(isOldWay) {
            // snore - still old clients
        } else {
            // yea! new clients are much greener!
        }
    }
}

PS - я говорю "интерфейс", хотя вы используете класс. Просто подумайте об интерфейсе как об общедоступных элементах класса, объявленных одновременно с реализацией класса. Обычно я предпочитаю код для интерфейсов, но это не вопрос ОП, и это не изменит мою точку зрения (просто где были сделаны модификации - особенно при использовании реализаций интерфейса Java 8 по умолчанию).

person Stevel    schedule 03.04.2016
comment
Я не согласен с тем, что Open to extension позволяет вам изменять интерфейс — иногда вам может понадобиться это сделать, но тогда это является нарушением OCP, а не его частью. OCP говорит, что если вам нужно провести рефакторинг и модификацию, вы должны сделать это таким образом, чтобы его можно было расширить в будущем. например через что-то вроде шаблона Посетитель/Стратегия. См. также эту статью дяди Боба - person anotherdave; 05.04.2016
comment
Ну, это немного щебетание, но я думаю, что пока клиентские коды не нужно переделывать, все в порядке (в лучшем случае перекомпилировать, но ни одна фигурная скобка не сглажена в праведную скобку ::smile: :). Правда, это что-то вроде нарушения чистого OCP, но я достаточно прагматичен, чтобы думать, что дух поддерживается полностью совместимыми изменениями в интерфейсе - не так ли? почему методы по умолчанию действительно вошли в язык? (Жаль, что мы не можем сделать их default final... ::haha::) - person Stevel; 05.04.2016
comment
конечно, прагматизм всегда является ключевым (именно поэтому это принцип, а не правило — его нарушение пахнет кодом, но это не значит, что этого никогда не следует делать). - person anotherdave; 12.04.2016

Это нарушение ОП. Близость к модификации означает, что после того, как вы реализовали класс и протестировали его, вы больше не должны его изменять. Бывают непредсказуемые случаи, когда бизнес-требования внезапно меняются, и вам приходится модифицировать свои классы, независимо от того, насколько хорошо вы их спроектировали. Самый чистый способ сделать это в вашем случае - добавить этот новый параметр, внедрив его в конструктор реализующих классов или, что еще лучше, внедрить поставщика этого параметра.

public class SomeClass{       
    IProvider _provider;

    public SomeClass(IProvider provider){
        _provider = provider
    }

    public void method1(){
        var someInput = _provider.Get();
        // do something
    }

    public void method2(){
        var someInput = _provider.Get();
        // do something
    }
}
person orso    schedule 02.05.2016