LINUX.ORG.RU

ООП - SRP, есть упоротость.

 


0

1

Есть класс книги.
Вот варианты использования:

  • Можно получить контент книги для прочтения ее.
  • При редактировании контента, книга сохраняется в БД.
  • Книгу можно получить только по заранее известному id (неважно откуда юзер узнает)
  • Можно распечатать на принтере. Любую книгу можно печатать.

Что заранее известно:
Никогда в жизни не будет нескольких хранилищ книг, и оно никогда не изменится.
Вот код:

class Book {
    private int id;
    private String content;
    private Database database;
    private Printer printer;

    public Book(int id) {
        database = new Database("table-books");
        printer = new Printer();
        this.content = database.select(id);
    }

    public String getContent() {
        return this.content;
    }
    
    public void setContent(String content) {
        this.content = content;
        save();
    }

    public void print() {
        // предположим, что тут сложная логика из 15 строк для работы с принтером. Но больше ни один класс печататься не умеет
        printer.selectPrinterAndPrint(getContent());
    }

    public void save() {
        // предположим тут сложная логика сохранения в бд, но больше ни один класс не сохраняется
        database.updateOrInsert(id, getContent());
    }
}

Но как гласит принцип, у класса должна быть только одна причина для изменения.
Получается, что любой чувак из интернетов, кто пишет умные статьи, меня с таким кодом пошлет куда подальше.
Какого черта мне здесь создавать отдельные классы Storage и PrintingSystem ради инкапсуляции логики туда? Если никто больше юзать не собирается их. Какого класс, который юзает Book я должен еще утяжелить знаниями о Storage и PrintingSystem?
Мне кажется, что все эти принципы должны учить только одному - здравому смыслу.

Перемещено leave из talks

Ответ на: комментарий от Booster

SOLID это вообще смех - несколько принципов выдранных ради того, чтобы получилось красивое слово.

Выстраданные практикой. Такой же словарик как и GoF. Позволяют не мычать в попытках доказать свою объективность коллегам.

A1
()

у тебя книга слишком много на себя берет, все эти setContent, save, getContent никакого отношения не имеют. Зачем нужны лишние функции? Представь себе автомобиль с функцией смены двигателя ... Весьма не практично ... Придется постепенно сменить весь автомобиль ...

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

setContent это больше builder, reader как угодно.

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

Можно задать модель с разумной степенью детализации.

Например: книга, частный случай носителя информации типа hard copy или что-то в этом роде ...

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

ОПушка, слушай в треде только этого ленивца плюс данный коммент. Все остальные тут (полу?)фрики.

// FizzBuzz пример на github просто пародирующая репа.

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

Выстраданные практикой.

Что значит «выстраданные практикой»? Это баззворд, который никакого отношения к реальности не имеющий.

Представим себе сказочную страну, где стоматологи лечат зубы через жопу. Идет время, прогресс не стоит на месте, умные инженеры придумывают зело мудреные техсредства для такого лечения. Уже дошли до того, что стоматолог, ставя пломбу через жопу, видит этот зуб на мониторе так, как будто он заглядывает поциэнту в рот, а поциэнт при манипуляциях не чувствует запах говна во рту. естественно, статистика улучшается, наверх бодро рапортуют, что методика подкреплена практикой, ДОКАЗАНО опытом, подтверждается статистикой. Для этой сказочной страны, данные факты являются ПРУФ ОФ КОНЦЕПТ.

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

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

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

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

индивидам с потенцией.

Это ты так энтерпрайзное быдло называешь? А Алан Кей, значит, потенцией не обладает? Я даже боюсь представить что тут под «творчеством» подразумевается, не буду спрашивать. Да и вообще, лучше не отвечай на это сообщение, творец.

callbackhell
()
Ответ на: комментарий от Solace

Кстати, странный какой-то осадок от твоего сообщения. Никогда мне этого не понять. Вроде и не дебил, а мозги все-равно набекрень.

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

Ты принципиально не можешь в аналогии, причем наивно полагаешь, что они что-то доказывают. Так вот, если вернуться в реальный мир, то текущие практики проектирования были «формализованы» людьми которые могли доставить конечный продукт. А кукаретики вроде тебя до сих пор верят, что вытаскивание экторов через жопу может совершить волшебство.

A1
()
Ответ на: комментарий от callbackhell

более нагляднее представить себе волшебный мир неосиляторов ...

anonymous
()

Я придумал еще пример:
Нам нужно сделать такой компонент, который бы показывал дату, а при нажатии на него появлялся диалог с выбором даты.

class DateEditView extends EditTextView implements DateEditDialog.OnDateListener {
    public void onClick() {
        DateEditDialog d = new DateEditDialog();
        d.setOnDateListener(this);
        d.show();
    }

    public void onDateListener(Date date) {
        this.setText(date.toString("dd-MM-yyyy"));
    }
}
Это компонент наследуется от простого поля ввода.
Затем при клике мы создаем прям в нем же диалог и назначаем себя слушателем события у диалога.
Вроде бы 2 обязанности: показывать дату, и уметь открывать диалог выбора даты для редактирования.
Что скажете?

pozitiffcat ★★★
() автор топика

Можно получить контент книги
Можно распечатать на принтере

В таком случае книге не требуется уметь себя печатать, это задача принтера.

anonymous
()

При редактировании контента, книга сохраняется в БД

Этим должен заниматься класс БД.

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

Вроде бы 2 обязанности

Вообще очень плохой термин responsibility. А на русский он вообще не в ту сторону переводится. В данном примере onClick и onDateListener тесно связаны по reason to change.

A1
()

В твоём примере ООП не имеет смысла. Тебе надо сделать структуру, содержащую в себе поля и отдельные процедуры для сохранения и чего угодно.

class Book {
    public int id;
    public String content;
}

public void print(Printer printer, Book book) {
        // предположим, что тут сложная логика из 15 строк для работы с принтером. Но больше ни один класс печататься не умеет
    printer.selectPrinterAndPrint(book.content);
}

public void save(Database database, Book book) {
    // предположим тут сложная логика сохранения в бд, но больше ни один класс не сохраняется
    database.updateOrInsert(book.id, book.content);
}
Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Как такой вариант:

class Book {
    private String content;
    private BookStorage storage;
    private BookPrinter printer;
    
    public void setContent(String content) {
        this.content = content;
        save();
    }
    
    public String getContetnt() {
        return this.content;
    }
    
    private void save() {
        storage.save(this);
    }
    
    public print() {
        printer.print(this);
    }
}

class BookStorage {
    private SqliteDatabase db;
    
    public void save(Book book) {
        // сложная логика по сохранению
    }
}

class BookPrinter {
    public void print(Book book) {
        // сложная логика
    }
}
Заморочка в том, что клиент класса book не хочет ничего знать о BookPrinter и BookStorage, для печати и сохранения при изменении.
Методы просто делегируют выполнение нужным классам.
Такой вариант нормальный? Или тут тоже нарушение SRP, кроме нарушения OCP?

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

Заморочка в том, что клиент класса book не хочет ничего знать о BookPrinter и BookStorage, для печати и сохранения при изменении.

Для этого есть IoC. Не хочет знать (и правильно не хочет) — пусть просто объявит у себя поле BookPrinter printer и использует его. А кто-нибудь умнее уже запишет в это поле то, что надо.

Методы просто делегируют выполнение нужным классам.
Такой вариант нормальный? Или тут тоже нарушение SRP, кроме нарушения OCP?

Ты пытаешься делать ОО-решение, когда книга сама умеет делать что-то осмысленное. Я (и некоторые другие в этом треде) придерживаются взгляда, что если язык умеет ООП (или ФП или ещё что-нибудь), это не означает, что весь код обязан быть написан в этом стиле. Надо выбирать подходящий для каждого случая. Для твоего случая, как мне кажется, подходящим стилем будет процедурный вариант.

Если даже делать ОО-решение, всё равно книга не должна держать у себя Printer и Storage, т.к. это не её зона ответственности, откуда она знает, куда её хотят сохранить. Это решает тот, кто её сохраняет. Если у тебя в проекте единственный Storage, проще его синглтоном сделать (замаскировав в паттерне «Service Locator», чтобы тестировать можно было) и пусть книга его использует.

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

Новая версия нихера не знает, кроме того что можно сохранять и печать. В обязанности класса входит сохранять при изменении и печатать, когда попросят. Но как это делать она не знает.

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

Вот такой пример:

class ComplexObject {
    private String field1;
    private int field2;
}

class ComplexObjectRepository {
    private SqliteDatabase db;
    private ComplexObjectSerializer serializer;

    public void save(ComplexObject object) {
        String serializedData = serializer.serialize(object);
        // сложная логика сохранения в БД
    }
}

class ComplexObjectSerializer {
    public String serialize(ComplexObject object) {
        // сложная логика сериализации в особый формат
    }
}
Здесь получается, что у ComplexObjectRepository 2 обязанности:
Сохранить в базу и уметь форматировать объект в нужный вид. Тут мне тоже извне держать сериализатор? И в репозиторий засунуть сразу строковое представление? В таком случае вообще отпадает смысл в репозитории...

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

Но как это делать она не знает.

это не освобождает от ответственности

anonymous
()

Ну вообще нормальный подход. Разделение на мелкие сущности есть зло. Потому что можно легко дойти до абсурда и вместо полноценной абстракции «человек» получить набор непонятных IГоер (а потом и IЛевыйГоер, IПравыйГоер), IХватаер и прочее.

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

Вообще, объект это штука, которая обладает состоянием и поведением, которое оперирует этим самым состоянием. И суть инкапсуляции заключается в том, чтобы состояние наружу не торчало. В примере с книгой инкапсуляция нарушена. Содержимое книги (а это состояние, как я понял) торчит наружу совершенно беспрепятственно. Ну сделать тогда его public полем и не заморачиваться.

Я бы сделал так:

class Book {
    private final int id;
    private final String content;

    // или вообще всю эту логику вынести в Database
    public static Book loadFrom(Database db, int id) {
        String content = db.select(id);
        return new Book(id, content);
    }

    public Book(int id, String content) {
        this.id = id;
        this.content = content;
    }

    public void printWith(Printer printer) {
        printer.selectPrinterAndPrint(content);
    }

    public void saveTo(Database db) {
        db.updateOrInsert(id, content);
    }
}
Если где-то в коде нужен content, то нужно понять, зачем он там нужен. Возможно, что это поведение вполне себе может (или должно) быть в классе Book (можно посмотреть на эту тему http://martinfowler.com/bliki/TellDontAsk.html, как раз про инкапсуляцию).

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

Страдал как-то tell don't ask и доходило до абсурда. Но вообще в целом эта идея больше ООП, чем все остальные.

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

Где абстракция это выражение бизнес моделей в коде так, как они изложены в описании предметной области.

Ню-ню. Нет никаких описаний предметной области. Есть бизнес задачи, и чем меньше ты потратишь времени на формализацию и напишешь рабочий код тем быстрее выкатишь фичу. Привет 90-ым.

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

Почему бы и нет. SaveTo интерфейс можно было бы использовать и для печати. Только название нужно придумать получше. Вот только все это Гуано очень сложно вяжется, когда нужно данные показать в GUI.

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

Эта идея говно, потому что нет ни одной причины почему loadFrom, printWith и saveTo должны быть в Book.

Равно как и нет причин почему их там быть не должно. Кроме того, что хрень какая-то получается, размазанная по всему проекту, а не книга.

Тестируется юнит тестами? Да. Позволяет изменять поведение печати и БД не трогая книгу? Да.

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

Равно как и нет причин почему их там быть не должно.

Внутре дергается один метод и это очень похоже на тупое делегирование, которого можно не делать. Все же просто.

A1
()
Ответ на: комментарий от pozitiffcat

Страдал как-то tell don't ask и доходило до абсурда. Но вообще в целом эта идея больше ООП, чем все остальные.

Фаулер об этом и пишет, что не стоит доводить до абсурда. Это не серебряная пуля.

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

Внутре дергается один метод и это очень похоже на тупое делегирование, которого можно не делать. Все же просто.

Внутре у ней неонка. Я не понял, что такое «тупое делегирование», но идея в том, что зависимости отдается состояние. Которое доступно только книге. И зависимость ваще не в курсе, кому она отдается и кто в нее печатает, сохраняет или что то там делает.

Но тема ООП холиварная ага :).

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

Вот только все это Гуано очень сложно вяжется, когда нужно данные показать в GUI.

Обычно вводят дополнительные абстракции, типа MVP (для формочек) и MVC (для веба). Где M это модели (например, книга), P штука, которая связывает между собой M и V. Ну, а V это морда. Хз, насколько это правильно.

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

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

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

А, ну может я неправильно понял.

Просто я встречал такую штуку. Презентер создает/получает модель и ей отдает интерфейс с методом showContent или printText или еще что-то в этом роде. А за интерфейсом скрывается Вид. В итоге ситуация получается сходна с принтером. Мы отдаем Вид, который может показывать текст, а текст это состояние модели. Которое она успешно отдает в сообщении printText. Слои получаются низкосвязными (Вид не знает о Модели, он только реагирует на сообщение вывода текста, не зная откуда он берется) и нет необходимости явно выдергивать состояние из Модели. И мне понадобилось совсем немного времени на то, чтобы понять, что происходит и закрыть задачу при таком подходе.

А вот как отображать текст - это зависит от Вида и от его параметров.

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

Но тема ООП холиварная ага

Вообще тут один товарищ правильно высказался. Дизайнить можно только реальные проблемы, то что в топике — детский сад.

A1
()
Ответ на: комментарий от pozitiffcat

Вообще пример FizzBuzz EE очень показательный. Из него можно вынести два урока. Первый состоит в том, что накладные расходы, связанные с внесением дополнительных уровней косвенности, довольно значительны. Нужно каждый раз оценивать, когда это даёт выгоду, а когда приводит к ненужной сложности. Второй урок более важен - нужно каждый раз думать головой, применяя любые принципы/паттерны/методики/ООП. Когда от них будет больше вреда, чем пользы, а когда наоборот? Когда стоит применять, а когда нет?

Если проанализировать песни кукаретиков, то можно заметить, что все они на один лад - «вот, посмотрите, я пытался не думая головой применить ваши принципы/паттерны, и получилось плохо, значит ваши принципы/паттерны говно»!

В этом выражается крах надежд кукаретиков на принципы/паттерны как на серебряную пулю. Когда надеешься, что принципы/паттерны заменят тебе умение проектировать, заменят тебе необходимость думать головой, но этого не случается. От этого и происходит разочарование - «ваши принципы говно».

Принципы/паттерны не заменяют умение проектировать, не заменяют голову. Слепое их применение ведёт к дополнительным расходам и следовательно к разочарованию и боли. Не повторяйте ошибок анонiмуса.

anonymous
()

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

с этого момента поподробнее. кто это сказал и где?

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

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