Спасибо автору за подробную статью с примерами. Есть несколько замечаний:
1. Введены лишние абстракции(ИМХО — класс CupOfBoiledWater). Как вариант, можно использовать этот код в примере для бойлера:
final class CupOfWater {
private final boolean powerAvailable;
public CupOfWater(boolean powerAvailable) {
this.powerAvailable = powerAvailable;
}
/** Return cups of water.
* @return Iterator<Water>
*/
public Iterator<Water> get(Integer numberOfCups) {
return (this.powerAvailable)? fill(numberOfCups, 90).iterator(): fill(numberOfCups, 10).iterator();
}
private List<Water> fill(Integer number, Integer temperature) {
List<Water>list = new ArrayList<Water>();
for(int i=0;i<number;i++) {
list.add(new Water(10L, temperature));
}
return list;
}
}
final class Water{
private final Long volume;
private final Integer temperature;
public Water(Long volume, Integer temperature) {
this.volume = volume;
this.temperature = temperature;
}
/** Return volume in millilitres of water.
* @return Long
*/
public Long volume() {
return this.volume;
}
/** Return temperature of water in Celsius scale.
* @return Integer
*/
public Integer temperature() {
return this.temperature;
}
@Override
public String toString() {
return "Water [volume=" + volume + ", temperature=" + temperature + "]";
}
}
final class Boiler {
private final CupOfWater water;
public Boiler(CupOfWater water) {
this.water = water;
}
public Iterator<Water> getCupOfWater() {
return water.get(2);
}
}
public class Main {
public static void main(String[] args) {
boilerTest();
}
public static void boilerTest() {
Iterator<Water>iterator = new Boiler(new CupOfWater(true)).getCupOfWater();
if(iterator.hasNext()) {
while (iterator.hasNext()) {
Water water = (Water) iterator.next();
System.out.println(water.toString());
if(water.temperature() > 10) {
// water is hot!
}else {
//water is cold!
}
}
}else {
System.out.println("water is not exists.");
}
}
}
2.
Но в большинстве практически интересных задач на вход подаются не простые переменные, а объекты. В том числе такие, которые могут принимать значение null.
Автор предполагает существование null в качестве параметра при вызове его методов. Я предлагаю его избегать/отказаться всеми способами и на уровне кода и на уровне архитектуры. Как это делать — другая тема.
3.
Если дождевая вода не собрана, то на входе мы имеем нулевой объект, иначе – нормальный объект.
И вот тут абстракции начинают течь. Бак имеет ёмкость с какой-то шкалой или объемом. Объем не может отсутствовать вообще у данного объекта так как это один из главных его параметров! Он может быть равным 0 и уже тем более не отрицательным. Он также может быть 1,2,3,4 и так далее. Если автор имел ввиду не бак, а подачу воды в водопроводе — там та же картина — вода хоть там, хоть там имеет свой объем в каких-то единицах. И он может быть только от 0 и выше. Но трактовку понятия «отсутствия воды» каждый видит по своему и в этом кроется ошибка. Я предлагаю вариант как «объект с параметром 0».
4.
Если запрашиваемого ресурса на момент запроса нет, и мы не хотим возвращать null, нам остается одно средство – выбросить Exception.
На этот счёт я говорил в прошлых комментариях — надо возвращать коллекцию, список, массив или итератор элементов. И тогда все будет хорошо.
Да, можно. Можно сделать и напрямую на ассемблере или даже на машине Тьюринга.
Я оценил Ваш тонкий логический ход. Отвечаю в том же ключе: А можно еще ввести новый объект с интерфейсами и абстракциями, самостоятельной проверкой на null или исполнением(да это же паттерн NullObject!). А можно просто все проверки на null написать в отдельном классе и добавить туда методы работы с объектом.( да это же наш Optional!) Это лучшее решение?
Передавать список или массив дальше я не считаю хорошей идеей, поскольку вызывающие клиенты могут не знать, что на самом деле ожидается строго 1 или 0 обьектов в списке.
Здесь проблемы нет — работайте всегда со списком или массивом. Также как работаете в других местах. Такое впечатление что эти клиенты не знают как работать со списком значений полученных из метода! Я же специально привел примеры кода работы со списком.
Но как говорил Торвальдс: “Talk is cheap. Show me the code.”
Посмотрим на Вашу вторую часть с примерами кода. Огромная просьба будет к Вам — приведите пожалуйста примеры когда методы возвращают один обьект и несколько обьектов. Например — тот же поиск обьектов по Id и нескольким Id. Во избежание манипуляций, а также для более объемного и подробного материала для дальнейшего обсуждения.
Почему это идея со списком или итератором или массивом так уж плоха(а уж в сравнении с Optional — это ещё два раза посмотреть!)? ПОЧЕМУ именно логически она плоха? На примере кофе-машины — разве она не может выдать НЕСКОЛЬКО порций кофе? И разве она так не делает? Но разберем тезисы подробнее:
Фактически мы принуждаем тем самым пользователей нашего метода на месте, сразу же после вызова функции разобраться со списком. Вряд ли он будет этот список передавать дальше как параметр других методов.
Нет, не принуждаем! Откуда это взято? И список и итератор или массив можно и потом передать в методы и сохранить и по сети передать — всё зависит от задачи.
Недостатком метода является его очевидная вычурность
В ЧЕМ КОНКРЕТНО эта «очевидная вычурность» проявляется? И как тогда проявляется «неочевидная невычурность»? К чему использовать такие «интересные» речевые обороты?
Уж очень сильно начинают различаться ситуации, когда get… метод возвращает элементарное значение, например int, заведомо существующий объект и условно существующий объект.
А тут вообще натягиваем сову на глобус! В коде, видите ли, сильно ситуации различаются с методами? В этом проблема? В том что есть методы которые возвращают РАЗНЫЕ значения? Так по моему они только этим и занимаются — на вход принимают значения, обрабатывают их и выдают результат на выходе. Разные на входе и разные на выходе.
А теперь практика — как это должно выглядеть например с итератором:
interface IMarks {
Iterator<String> findById(Long ... ids);
void print(PrintStream ps, Iterator<String> values);
void printOne(PrintStream ps, String value);
}
public final class Marks implements IMarks{
Map<Long, String> test = new HashMap<>();
{
test.put(1L, "1");
test.put(2L, "2");
test.put(3L, "3");
test.put(4L, "4");
test.put(5L, "5");
}
@Override
public Iterator<String> findById(Long... ids) {
List<Long>idsList = Arrays.asList(ids);
List<String> list = new ArrayList<String>();
for(Entry<Long, String>entry : test.entrySet()) {
if(idsList.contains(entry.getKey())) list.add(entry.getValue());
}
return list.iterator();
}
public static void main(String[] args) {
IMarks marks = new Marks();
Iterator<String>it = marks.findById(2L, 3L, 5L);
if(it.hasNext()) marks.printOne(System.out, it.next());
if(it.hasNext()) marks.print(System.out, it);
}
@Override
public void print(PrintStream ps, Iterator<String> values) {
while (values.hasNext()) {
ps.println(values.next());
}
}
@Override
public void printOne(PrintStream ps, String value) {
ps.println(value);
}
}
Смотрите метод main. Сравните сигнатуры методов print и printOne.
Ну а если использовать аналогию с кофе, то будет примерно так:
Iterator<CoffeePortion> coffeePortions() // или
List<CoffeePortion> coffeePortions()
А вишенкой на торте последний вопрос — так зачем нужен Optional, если и так можно делать?
1. Введены лишние абстракции(ИМХО — класс CupOfBoiledWater). Как вариант, можно использовать этот код в примере для бойлера:
2. Автор предполагает существование null в качестве параметра при вызове его методов. Я предлагаю его избегать/отказаться всеми способами и на уровне кода и на уровне архитектуры. Как это делать — другая тема.
3. И вот тут абстракции начинают течь. Бак имеет ёмкость с какой-то шкалой или объемом. Объем не может отсутствовать вообще у данного объекта так как это один из главных его параметров! Он может быть равным 0 и уже тем более не отрицательным. Он также может быть 1,2,3,4 и так далее. Если автор имел ввиду не бак, а подачу воды в водопроводе — там та же картина — вода хоть там, хоть там имеет свой объем в каких-то единицах. И он может быть только от 0 и выше. Но трактовку понятия «отсутствия воды» каждый видит по своему и в этом кроется ошибка. Я предлагаю вариант как «объект с параметром 0».
4.
На этот счёт я говорил в прошлых комментариях — надо возвращать коллекцию, список, массив или итератор элементов. И тогда все будет хорошо.
Я оценил Ваш тонкий логический ход. Отвечаю в том же ключе: А можно еще ввести новый объект с интерфейсами и абстракциями, самостоятельной проверкой на null или исполнением(да это же паттерн NullObject!). А можно просто все проверки на null написать в отдельном классе и добавить туда методы работы с объектом.( да это же наш Optional!) Это лучшее решение?
Здесь проблемы нет — работайте всегда со списком или массивом. Также как работаете в других местах. Такое впечатление что эти клиенты не знают как работать со списком значений полученных из метода! Я же специально привел примеры кода работы со списком.
Но как говорил Торвальдс: “Talk is cheap. Show me the code.”
Посмотрим на Вашу вторую часть с примерами кода. Огромная просьба будет к Вам — приведите пожалуйста примеры когда методы возвращают один обьект и несколько обьектов. Например — тот же поиск обьектов по Id и нескольким Id. Во избежание манипуляций, а также для более объемного и подробного материала для дальнейшего обсуждения.
Нет, не принуждаем! Откуда это взято? И список и итератор или массив можно и потом передать в методы и сохранить и по сети передать — всё зависит от задачи.
В ЧЕМ КОНКРЕТНО эта «очевидная вычурность» проявляется? И как тогда проявляется «неочевидная невычурность»? К чему использовать такие «интересные» речевые обороты?
А тут вообще натягиваем сову на глобус! В коде, видите ли, сильно ситуации различаются с методами? В этом проблема? В том что есть методы которые возвращают РАЗНЫЕ значения? Так по моему они только этим и занимаются — на вход принимают значения, обрабатывают их и выдают результат на выходе. Разные на входе и разные на выходе.
А теперь практика — как это должно выглядеть например с итератором:
Смотрите метод main. Сравните сигнатуры методов print и printOne.
Ну а если использовать аналогию с кофе, то будет примерно так:
А вишенкой на торте последний вопрос — так зачем нужен Optional, если и так можно делать?