Столкнулся тут с такими принципами:
— метод должен принимать параметры как можно более общего типа
— метод должен возвращать объект как можно более конкретного класса
Первый принцип, можно сказать, классика и позволяет избегать большой связности. Со вторым же у меня трудности с пониманием приемуществ такого подхода(недостаток в виде большей связности весьма очевиден).
Рассмотрим на примере (здесь и далее Java, хотя это особого значения вроде как и не имеет)
'Классический' вид:
List<String> getNames(Long parentId, List<String> params) {
List<String> result = new ArrayList<String>(20); // нам доподлинно известно что будет не больше 20 элементов
// заполнение resultreturn result;
}
'Конкретный' вид:
ArrayList<String> getNames(Long parentId, List<String> params) {
List<String> result = new ArrayList<String>(20); // нам доподлинно известно что будет не больше 20 элементов
// заполнение resultreturn result;
}
Модификатор доступа я умышленно опустил, чтобы остался повод для дискуссии применимо ли это для public или может только private методов. На мой взгляд общедоступные методы не должны ни в коем случае быть 'конкретными'.
Дальнейшие примеры и случаи для 'конкретного' вида.
Теперь вариант кода когда нам все равно конкретный класс или интерфейс в сигнатуре метода, 'не опасный':
void method1() {
ArrayList<String> names = getNames(...);
// делаем что-то, но за рамки интерфейса List не выходим. Но зачем тогда объявление names как ArrayList :) ?
}
Ну и на закуску, 'опасный' код:
void method1() {
ArrayList<String> names = getNames(...);
// делаем что-то
names.trimToSize(); // специфический для ArrayList метод
// делаем что-то
}
И вот наступает момент, когда нам приходится менять в getName ArrayList на LinkedList.
— В случае 'не опасного' кода ничего менять не надо. Но тогда вопрос зачем в сигнатуре метода указывать конкретный класс?
— 'среднеопасный' код: меняем в коде имя одного класса на другое и больше ничего не надо делать. И опять вопрос: зачем и почему такое может понадобиться, если с интерфейсами был бы точно такой код?
— 'опасный код': в этом случае мы отгребаем за связность и нам приходится не только менять тип переменной, но и переписывать логику. Тут я могу придумать для чего так можно было сделать: например, для оптимизации когда известно, что конкретная рализация в данном случае будет эффективнее намного другой.
Резюмируя все примеры и мысли: Возвращать конкретный класс, а не интерфейс в общем случае скорее вредно, чем полезно и применять такой принцип для всех методов лучше не стоит.
Не упустил ли я какую-то деталь, которая доказывает полезность 'конкретного' принципа?
Здравствуйте, Dziman, Вы писали:
D>Столкнулся тут с такими принципами: D>- метод должен принимать параметры как можно более общего типа D>- метод должен возвращать объект как можно более конкретного класса
D>Первый принцип, можно сказать, классика и позволяет избегать большой связности. Со вторым же у меня трудности с пониманием приемуществ такого подхода(недостаток в виде большей связности весьма очевиден).
...
D>Ну и на закуску, 'опасный' код: D>
D>void method1() {
D> ArrayList<String> names = getNames(...);
D> // делаем что-то
D> names.trimToSize(); // специфический для ArrayList метод
D> // делаем что-то
D>}
D>
D>- 'опасный код': в этом случае мы отгребаем за связность и нам приходится не только менять тип переменной, но и переписывать логику. Тут я могу придумать для чего так можно было сделать: например, для оптимизации когда известно, что конкретная рализация в данном случае будет эффективнее намного другой.
D>Не упустил ли я какую-то деталь, которая доказывает полезность 'конкретного' принципа?
Упустили, причём в своём же примере.
В примере наличие конкретного типа ArrayList позволяет делать trimToSize() без извращений по пересозданию списка.
В общем случае — эти принцпы симметричны:
— метод должен принимать параметры как можно более общего типа — для того, чтобы методу можно было подсунуть как можно большее количество типов, что делает метод более универсальным.
— метод должен возвращать объект как можно более конкретного класса — для того, чтобы с результатом его работы можно было сделать как можно большее количество действий без дальнейших преобразований, что, опять-таки, сделает метод более универсальным.
Во втором случае сторонним эффектом, действительно, является увеличение связности (если возвращаемый класс не библиотечный), но тут уж дело программиста решить, что ему в данном случае важнее.
Кстати, в первом случае присутствует аналогичный недостаток, но он не такой очевидный и не так часто встречается: если при расширении метода нам понадобится от его параметров что-то большее, чем то, что мы заявили в параметрах изначально, то нам точно так же придётся переписывать вызывающий код.
Re: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Здравствуйте, Dziman, Вы писали:
D>И вот наступает момент, когда нам приходится менять в getName ArrayList на LinkedList. D>- В случае 'не опасного' кода ничего менять не надо. Но тогда вопрос зачем в сигнатуре метода указывать конкретный класс? D>- 'среднеопасный' код: меняем в коде имя одного класса на другое и больше ничего не надо делать. И опять вопрос: зачем и почему такое может понадобиться, если с интерфейсами был бы точно такой код? D>- 'опасный код': в этом случае мы отгребаем за связность и нам приходится не только менять тип переменной, но и переписывать логику. Тут я могу придумать для чего так можно было сделать: например, для оптимизации когда известно, что конкретная рализация в данном случае будет эффективнее намного другой.
D>Резюмируя все примеры и мысли: D>Возвращать конкретный класс, а не интерфейс в общем случае скорее вредно, чем полезно и применять такой принцип для всех методов лучше не стоит. D>Не упустил ли я какую-то деталь, которая доказывает полезность 'конкретного' принципа?
Давайте напишем не "классический и конкретный", а "абстрактный и конкретный", и уже отсюда будем вести умозаключения.
Абстрактный класс является базовым для конкретного класса, и наоборот конкретный является наследуемым от базового.
Рассматриваемые методы используются вне абстрактного и конкретного класса.
Теперь вопрос, какой метод вам нужен:
1. Абстрактный — с конкретными (или, если позволено, ещё и абстрактными) объектами работает через интерфейс абстрактного
2. Конкретный — с конкретными объектами работает через интерфейс конкретного
Если внутри метода происходит попытка преобразования в конкретный объект будем считать этот метод конкретным.
Слова "опасный" или "среднеопасный" в данном случае звучат не совсем понятно, так как в проектировании важно знать какого поведения хочешь добиться.
Если нужна работа через абстрактный интерфейс, тогда понятное дело нигде нельзя использовать конкретные объекты напрямую, а вот косвенно через абстрактный интерфейс сколько угодно.
Если это не принципиально, тогда можно и получать и возвращать абстрактные или конкретные объекты, но помнить о том, что метод ограничен в применении.
Внутри такого метода должна быть проверка успешно ли прошло преобразование из абстрактного в конкретный объект или нет, тогда это будет "не опасно".
А если используются конкретные объекты, тогда вопросов вообще нет, так как преобразование не требуется.
По поводу принципов сказал бы так, важно понимать поведение системы в различных вариантах. Нет смысла выводить некое общее правило, лучше полагаться на здравый смысл в каждом конкретном случае.
Re: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Интерфейс класса определяется его клиентом, т.е. классом который его использует. Если клиентам класса для своей работы достаточно будет будет интерфейса, то его и следует возвращать.
Re[2]: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Здравствуйте, Rinbe, Вы писали:
R>Интерфейс класса определяется его клиентом, т.е. классом который его использует. Если клиентам класса для своей работы достаточно будет будет интерфейса, то его и следует возвращать.
Скорее возможными потенциальными клиентами.
Re: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Здравствуйте, Dziman, Вы писали:
D>Столкнулся тут с такими принципами:
достаточно субъективно и зависит от применения и семантики. Я бы доавбил так, что как можно более конкретный интерфейс которого хватит всем, а не самую глубокую реализацию интерфейса в иерархии.
Как пример имеем иерархию IEnumerable <|--- Collection <|--- Array <|--- FileArray
FileArray массив на диске.
Так вот Если клиентам хватит Collection или Array то не имеет смысла возвращать FileArray, т.к. более специфичный интерфейс и данный тип добавит связанности вашему модулю, т.к. каждый клиент который вызывает этот метод вынужден будет ссылаться на FileArray, который за собой потащит еще кучу зависимостей.
Re: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Здравствуйте, Dziman, Вы писали:
D>Ну и на закуску, 'опасный' код: D>
D>void method1() {
D> ArrayList<String> names = getNames(...);
D> // делаем что-то
D> names.trimToSize(); // специфический для ArrayList метод
D> // делаем что-то
D>}
D>
D>И вот наступает момент, когда нам приходится менять в getName ArrayList на LinkedList.
С практической точки зрения я бы возвращал наиболее общий тип, List, а то и Collection или Iterable, исходя из моего текущего понимания предметной области — нужен ли будет клиентам специфические методы.
Если же вдруг какому-то клиенту понадобятся специфические методы, то совершенно безопасно конкретизировать возвращаемый тип, это никак не повлияет на существующий код, т.к. полностью backward-compatible. Если делать наоборот, то пространства для манёвров меньше.
Это, конечно, в том случае если из текущего понимания предметной области никак нельзя точно определить точное имя типа.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
D>>Столкнулся тут с такими принципами: _>достаточно субъективно и зависит от применения и семантики. Я бы доавбил так, что как можно более конкретный интерфейс которого хватит всем, а не самую глубокую реализацию интерфейса в иерархии.
_>Как пример имеем иерархию IEnumerable <|--- Collection <|--- Array <|--- FileArray _>FileArray массив на диске.
_>Так вот Если клиентам хватит Collection или Array то не имеет смысла возвращать FileArray, т.к. более специфичный интерфейс и данный тип добавит связанности вашему модулю, т.к. каждый клиент который вызывает этот метод вынужден будет ссылаться на FileArray, который за собой потащит еще кучу зависимостей.
Надо смотреть конкретные случаи. Иначе потом через 2-3 передачи этого параметра возникают монстрики типа var fa = arg as FileArray или var fa = new FileArray(arg).
Re: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Здравствуйте, Dziman, Вы писали:
D>Столкнулся тут с такими принципами: D>- метод должен принимать параметры как можно более общего типа D>- метод должен возвращать объект как можно более конкретного класса
— Дизайн чего угодно должен определяться требованиями потребителя
Re[2]: Возвращать или не возвращать из метода конкретный класс, а не интерфейс
Здравствуйте, andyag, Вы писали:
D>>- метод должен принимать параметры как можно более общего типа D>>- метод должен возвращать объект как можно более конкретного класса A>- Дизайн чего угодно должен определяться требованиями потребителя
Это многие понимают. Ещё важно: Дизайн должен ограничиваться требованиями потребителя. Универсальные всемогуторы тоже не сахар.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай