LINUX.ORG.RU

Геттеры и сеттеры - зло. А что дальше?

 , , , ,


1

4

Читаю мысли Егора Бугаенко https://www.yegor256.com/2016/04/05/printers-instead-of-getters.html о том что геттеры - зло. Что-то похоже высказывал Аллен Голуб: https://www.javaworld.com/article/2073723/why-getter-and-setter-methods-are-e... .

Краткая мысль: объект не должен раздавать свои внутренние данные налево-направо. Поэтому и геттеров не должно быть.

Но вот что не даёт покоя. Как реализовать при этом подходе простейший use-case:

Есть книжный магазин BookStore. Требуется узнать какие в нём есть книги автора по его фамилии.

«Неправильный» и простейший поход, который напишет 9 из 10 разработчиков (язык неважен, хоть со стримами в java, всё одно в коде буду геттеры):

class BookStore
{
    List<Book> searchByAuthor(String author)
    {
        List<Book> found;

        for (int i = 0; i < this.books.length; i++) {
            // EVIL
            if (this.books[i].getAuthor() == author) {
                found.append(this.books[i]);
            }
        }

        return found;
    }
};


Не пойму как реализовать этот use-case следуя парадигме вышеуказанных авторов без геттеров?

class BookStore
{
    List<int> searchByAuthor(String author)
    {
        List<int> found;

        for (int i = 0; i < globalData.books.length; i++) {
            if (globalData.books[i].author == author) {
                found.append(i);
            }
        }

        return found;
    }
};
deep-purple ★★★★★ ()

на лямбдах и монадах, как в скале - вызываешь метод, в параметрах передаешь метод, который принимает результат. Говорят, этот подход называется Tagless Final. Но это еще (для меня) неточно.

bvn13 ★★★★★ ()
Последнее исправление: bvn13 (всего исправлений: 1)

Ты лучше передай мысль умных людей, пожалуйста.

объект не должен раздавать свои внутренние данные налево-направо

почему? что их смущает?

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

почему? что их смущает?

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

former_anonymous ★★★ ()

очевидно же

class BookStore
{
    String searchByAuthor(String author)
    {
        String xml = this.toXML();
        List<Book> books = some_xml_xpath_kungfu(xml, author);

        StringBuidler b = new StringBuilder();
        b.append("<books>");
        for (book in books)
            b.append(book.toXML());
        b.append("</books>");
        return b.toString();
    }

    String toXML() {
        StringBuidler b = new StringBuilder();
        b.append("<books>");
        for (book in this.books)
            b.append(book.toXML());
        b.append("</books>");
        return b.toString();
    }
};
anonymous ()
Ответ на: комментарий от bvn13

Ты просто выдумываешь способ или действительно считаешь, что в этой наркомании есть смысл?

WitcherGeralt ★★ ()
Последнее исправление: WitcherGeralt (всего исправлений: 1)
Ответ на: очевидно же от anonymous

это первое что приходит в голову, ОДНАКО мы должны ЗНАТЬ как именно сериализован xml, чтобы натравливать на него xpath. Что если автор в XML хранится не как «author», а «book-author»? Откуда мы вообще знаем как именно хранится там автор? Это не годится.

former_anonymous ★★★ ()
Ответ на: комментарий от pon4ik

ещё мощнее нарушаешь инкапсуляцию?

Да. Но ведь... она в принципе нафиг не нужна, кроме как защита от кривых ручек копошащихся в коде. Инкапсуляция — лишь инструмент договорённости а-ля «вот это не трожь!».

Не принимай близко к сердцу, но задумайся.

deep-purple ★★★★★ ()
Ответ на: комментарий от former_anonymous

Откуда мы вообще знаем как именно хранится там автор?

Валидируй XML нужной XSD schema.

deep-purple ★★★★★ ()
Ответ на: комментарий от deep-purple

Public переменная, не наруешает принцип инкапсуляции?

Или ты предлагаешь создать «property» ?

class Prop {
privat:
 somedata &somedata;
 somemagic somemegic();
public:
 Prop(somedata &somedata) : somedata(somedata) {}

 bool operator == (const string &rhs) {
   return somemagic == rhs;
 }

}

...

class Book {
  somedata somedata;
...
public:
  Book() : Author(somedata);

  Prop Author;
}

for (const auto &i : books) {
  if (i.author == author) {
    result.append(i);
  }
}

sparks ★★★ ()
Ответ на: комментарий от former_anonymous

это первое что приходит в голову, ОДНАКО мы должны ЗНАТЬ как именно сериализован xml, чтобы натравливать на него xpath. Что если автор в XML хранится не как «author», а «book-author»? Откуда мы вообще знаем как именно хранится там автор?

Из документации.

anonymous ()
Ответ на: комментарий от sparks

Я предлагаю создать глобальный синглтон данных, доступный отовсюду. Но повторюсь:

Не принимай близко к сердцу, но задумайся.

deep-purple ★★★★★ ()

Требуется узнать какие в нём есть книги автора по его фамилии

Кому требуется узнать?

Краткая мысль: объект не должен раздавать свои внутренние данные налево-направо. Поэтому и геттеров не должно быть

Как бы общее правило, что объект должен реализовать только минимально необходимый публичный интерфейс давно известно. Разумеется не должно быть геттеров (и вообще публичных методов) просто «шоб було».

class BookStore

У тебя неправильный пример. Если объект выполняет функции контейнера, то разумеется что набор геттеров, сеттеров и итераторов это его основной интерфейс.

no-such-file ★★★★★ ()
Ответ на: комментарий от former_anonymous

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

Да и вообще, почему бы не воспринимать (в контексте только ридонли) геттеры как просто публичные проперти? Что так, что эдак. Ах да, ну пусть с копированием. Что мешает, когда нужна копия, вызывать а-ля «getCopyOf(data)»?

deep-purple ★★★★★ ()
class Book {
    private String author;

    public boolean matchesAuthor(String author) {
        return this.author.equals(author);
    }
}

Дальше, думаю, очевидно.

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

Legioner ★★★★★ ()

Не надо слушать всякую шушару и/или не надо воспринимать её слова буквально. Если удобно использовать геттеры и сеттеры, надо использовать. А в приведённом примере вообще попахивает dataclass’ами.

slovazap ★★★★★ ()
Ответ на: комментарий от no-such-file

Кому требуется узнать?

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

Если объект выполняет функции контейнера

Вот тут соглашусь. Дельное замечание. Но нужны ли такие объекты? Или такие объекты создаются, как ты сам сказал:

просто «шоб було»

ну или как деды завещали. Не, деды херни не посоветуют, в основном.

deep-purple ★★★★★ ()
Ответ на: комментарий от no-such-file

Разумеется не должно быть геттеров (и вообще публичных методов) просто «шоб було»

Автор утверждает, что их не должно быть вообще.

I’m not saying that get/set should be avoided when possible. No. I’m saying that you should never have them near your code.



https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html

ты утверждаешь, что это не так (а именно что геттеры иногда нужны), но меня интересует реализация задачи без геттеров согласно вышеперечисленным авторам.

former_anonymous ★★★ ()
Ответ на: комментарий от former_anonymous

утверждает

Пусть. Это крайность, а крайность это плохо.

реализация задачи без геттеров

Почему у меня геттер?

согласно авторам

Да плыли бы они дальше, по трубам, в очистные...

deep-purple ★★★★★ ()

Егора Бугаенко

Когда-то попалось видео с какой-то конференции с этим челом, сначала он сказал, что никогда не писал код за деньги а потом начал нести какую-то дичь.

ya-betmen ★★★★★ ()

Хоспади, да ты спроси сам объект этот ли автор у него.

class Book {
...
boolean hasAuthor(Author author){
    return this.author.equals(author);
}
...
}
Deleted ()
Ответ на: комментарий от Deleted

Неа!!! Пусть автор не строка, а тоже объект, где есть имя, фамилия, дата рождения и всё такое, да и авторов у книги может быть несколько. Изобразишь у кого там как и что спрашивать?

deep-purple ★★★★★ ()
Ответ на: комментарий от deep-purple

Но нужны ли такие объекты?

По большому счёту нет. Но в конкретно взятом языке это может быть полезно.

Пользователю, который указал нужный ему фильтр

Указал фильтр где? BookStore должен не возвращать список, а отрендерить результат. Иначе BookStore это велосипедный контейнер и ненужен.

no-such-file ★★★★★ ()
Ответ на: комментарий от no-such-file

в конкретно взятом языке это может быть полезно

В конкретно взятом языке это может быть единственный доступный путь реализации. К сожалению.

BookStore должен не возвращать список, а отрендерить результат

Нет, рендерит тот, кто список запрашивал. А этот лишь вернул список подошедших. Да и вообще — рендеру надо бы отдать копию данных, иначе лочить придётся, т.к. может пока он 10к позиций рендерит, пользак уже удалил одну книгу и список требуется перестроить, вобщем UB.

deep-purple ★★★★★ ()
Ответ на: комментарий от former_anonymous

что их не должно быть вообще

Тогда и переменных не должно быть вообще. Потому что переменная это «объект» с одними только геттером и сеттером. Каждая идея имеет границы применимости, не стоит возводить её в абсолют.

no-such-file ★★★★★ ()
Ответ на: комментарий от deep-purple

Нет, рендерит тот, кто список запрашивал. А этот лишь вернул список подошедших

А зачем тогда нужен BookStore? Храним просто в списке, кому надо сам поищет как ему надо. Вообще даже отдельный класс поисковиков лучше уж завести, если это нужно регулярно.

no-such-file ★★★★★ ()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от former_anonymous

это пример без геттера? :)

а где тут геттер? book.author это доступ к полю данных. если твоей логикой пользоваться, то и 2+2 не сложить) как ты собрался получать значение переменной тогда?

anonymous ()
Ответ на: комментарий от deep-purple

Разупорись, автор тоже объект в моем примере.

class Book {
private final Set<Author> authors;
...
boolean hasAuthor(Author author){
    return this.authors.contains(author);
}
...
}

equals у Author сам напишешь.

Deleted ()
Ответ на: комментарий от no-such-file

Верно. Один большой синглтон данных доступный со всего приложения. Всё — условие «без геттеров» выполнено.

deep-purple ★★★★★ ()
Ответ на: комментарий от Legioner

Дальше, думаю, очевидно.

Да, верно. Размышлял о таком подходе. С этим подходом абсолютно вся логика, относящаяся к Book, должна быть упакована в Book. Такой подход в какой-то момент времени создаст Book с сотней методов, что уже нарушает принципы SOLID (Принцип единственной ответственности, Принцип разделения интерфейса). Видимо это то что авторы и хотят, хз.

former_anonymous ★★★ ()
Последнее исправление: former_anonymous (всего исправлений: 1)
Ответ на: комментарий от former_anonymous

Ну зато все операции рядом с данными, всё по канонам ООП.

Legioner ★★★★★ ()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от deep-purple

Неа!!! Пусть автор не строка, а тоже объект, где есть имя, фамилия, дата рождения и всё такое, да и авторов у книги может быть несколько. Изобразишь у кого там как и что спрашивать?

class Book {
...
template <typename R, typename Op, typename Field, typename T>
R hasAuthor(const T &value){
    return Op::apply(this->myvalueof(Field()), value);
}
...
}
anonymous ()
Ответ на: комментарий от former_anonymous

нарушает принципы SOLID

Почему? Это же всё в контексте книги, даже если фигурирует имя автора. Ведь у книги есть автор.

deep-purple ★★★★★ ()

Краткая мысль: объект не должен раздавать свои внутренние данные налево-направо. Поэтому и геттеров не должно быть.

графоманы первооткрыватели блин. основы ооп - объект хранит данные и их поведение. если в объекте не заложено поведения, то там геттеры нормально, т.е. это тупое хранилище.

if (this.books[i].getAuthor() == author) {}
-> может быть разным из разного заложенного поведения. Возможен такой вариант.
if (this.books[i].isAuthor(author)) {}

Но на самом деле, тут ошибка в самом подходе List<Book> found; по хорошему должно быть выделено в отдельный объект BookLibrary, опять же исходя из логики приложения. Всё ооп это логика.

vtVitus ★★★★★ ()
Ответ на: комментарий от deep-purple

Один большой синглтон данных

Какой синглтон, при чём тут синглтон? Храни книжки в ArrayList<Book>, или что там удобнее. В чём смысл класса BookStore?

no-such-file ★★★★★ ()
Ответ на: комментарий от deep-purple

при таком подходе ты фигачишь всю бизнес-логику в класс Book. Абсолютно всю, которая может быть в твоём проекте. Поиск по автору, по части ISBN и т.д. Всего лишь какая-то книга с течением времени станет толстенным классом, нарушающим принципы SOLID, как я написал выше.

former_anonymous ★★★ ()

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

anonymous ()
Ответ на: комментарий от no-such-file

Кроме книг есть авторы, их надо как-то связать между собой. У этой книги вон те авторы, а у того автора вон те книги. Только данные. Дерево. Одно. Потому синглтон.

В чём смысл класса BookStore?

Мы ж добеседовали обоюдно что смысла в нём нет. Зачем ты спрашиваешь?

deep-purple ★★★★★ ()
Ответ на: комментарий от deep-purple

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

pon4ik ★★★★★ ()
Ответ на: комментарий от former_anonymous

Не, там теперь нужны просто маленькие функции, которые знают каждая своё маленькое дело: получить книгу, продать книгу, найти книги по автору и т.д. Если есть желание — группируешь функции по общим назначениям так, как желаешь и получаешь несколько классов, ничего не хранящих, но имеющих методы умеющие работать с целевыми данными.

deep-purple ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.