Это как если 10 человек будут играть в шахматы против чемпиона мира (по шахматам) путём голосования за каждый ход — что их 10, что 100, что 2 — разницы нет, точно сольют.
Меня бесило, что пока я сидел до ночи, изучая F#, моя дочь начала называть папами всех мужиков вокруг. А этот чувак вместо того, чтобы качаться, спокойно уходил домой к детям. И мне хотелось его наказать.
Сидеть до ночи изучая F# — это совсем не то же самое, что сидеть до ночи разгребая баги в коде того товарища. Качество вашего времени будет разным. Изучать F# — круто, а фиксить баги — это хрень. Люди вроде вас, автор, как раз в итоге и фиксят баги, и анализируют причины их возникновения, и исходя из этих годами накопленных знаний дают толковый фидбэк на код ревью. Это совершенно нормально — уважать себя, свои знания и опыт, ценить своё время и стараться избегать предсказуемых граблей.
Логика такого масштаба развесистости не имеет права быть реализованной в одном методе. Просто посмотрите на количество "компонентов" — a, b, c, F0, F1, F2, F3, F4, do_something_with(), res, и т.д. Если внутри одного метода действительно такой богатый контекст, это означает, что проблема не в самом методе, а в его окружении. Будет этот метод таким как вы его написали (с новым оператором) или в 3 раза длинее (без нового оператора), разницы совершенно никакой: он хренов бай дизайн.
Императивный подход — КАК добиться нужного результата. Например: button.setBackgroundColor(RED) (есть эта строчка кода, которая выполняется до каких-то других и после каких-то других; эта строчка — это команда что-то сделать — "установить цвет фона")
Декларативный подход — ЧТО собой представляет нужный результат. Например: <button backgroundColor="red">... (это не команда — мы не говорим "во-первых я хочу кнопку, а во-вторых — давайте ей установим красный цвет фона"; вместо этого мы описываем результат — кнопка с красным фоном)
Ваш пример для декларативного подхода "приготовь яичницу" — это на самом деле императивный подход, т.к. вы описываете не результат, а способ его достижения. Декларативно яичница выглядит вот так: "яичница".
Что делать, например, если мне на кнопку надо несколько обработчиков повесить?
Написать _handleClick таким образом, чтобы он дёргал несколько обработчиков. Вы вопрос неправильно ставите. Обработчик всегда один: нажатие кнопки означает, что юзер "даёт команду" вашему аппликейшну. Как вы там будете эту команду обрабатывать — одним хендлером или десятком — ваше дело. Кнопка со своей задачей справилась — донесла до вас что её нажали.
Всю мою сознательную дейятельность JS-разработчика вокруг твердили "нехорошо смешивать логику и представление"
Вы путаете "логику" и "бизнес-логику". Спрятать кнопку или показать в зависимости от аутентифицированности пользователя — это как раз "логика представления". Будет это if внутри jsx, или это будет непонятная закорючка в handlebars — вообще никакой разницы.
Вы путаете "представление" и "мой любимый шаблонизатор". Представление — это часть кода, которая "оформляет" ответ прежде чем ответ уйдёт к тому, кто прислал запрос. Код сериализации объекта в JSON — это такое же представление, как и натягивание модели на шаблон с целью получить HTML. Даже больше — когда вы пишете код, который через HTTP возвращает 200 — это тоже представление.
Какая-то длинная статья. Чтобы понять фреймворк это или библиотека, достаточно ответить на вопрос "управляет ли оно потоком выполнения моей программы?". Если да, то это фреймворк, если нет, то это библиотека.
Если вопрос кажется дурацким или непонятным, его можно переформулировать вот так: "в своём коде я отвечаю на вопросы или скорее делаю утверждения?". Первое — использование фреймворка, второе — использование библиотеки.
Меня всегда удивляло стремление отдельных людей выдумывать «правила», а потом героически их преодолевать!
Я не пропагандирую это "правило параноика" и сам ему не следую. Не это конкретное правило, но правила вообще имеют смысл когда уровень квалификации "среднего" участника команды приводит к регулярным наступаниям на грабли. С точки зрения поставки фич бизнесу всегда проще сказать "всегда делай вот так", чем ждать пока коллега соберёт все грабли, пропустит все дедлайны, научится и станет делать "правильно" осознанно.
И если библиотека хорошо выполняет свою задачу, пусть она трижды вся public, я буду ее использовать, и скажу ее разработчику лишь спасибо!
Ну не всё так просто:
У разработчика при таком подходе будут связаны руки: т.к. всё public и всё часть контракта библиотеки, нельзя будет просто так пойти и отрефакторить что-то. Каждый новый класс — это minor релиз, каждое переименование любого класса — это major релиз. Patch релизы будут предполагать только изменения кода реализации существующих классов и методов.
Вы в итоге разработчику спасибо не скажете, т.к. на ранних стадиях развития библиотеки каждый второй релиз скорее всего будет major — с изменённым контрактом.
Я ни в коем случае не говорил, что нужно делать приватные классы по умолчанию. Перечитайте пожалуйста: "если вы до конца не определились как вам оформить класс, то делайте его приватным...". Если вы хотите сделать класс частью интерфейса вашей библиотеки, это просто означает что вы определились.
Я же написал в явном виде — "нужна причина" :-) У вас причина есть, соответственно вы осознанно будете делать классы/методы/конструкторы паблик. Но не у всех есть такая причина.
Это ни в коем случае не АОП. Это самое обычное ООП + делегирование.
Это верно только для простых алгоритмов… Если это более сложный класс, который, тем более, должен иметь доступ к приватным полям объекта, ваш подход ещё хуже
Посмотрите примеры использования паттерна "Стратегия" — это собственно делегирование в чистом виде и есть.
Бывают случаи, когда базовый класс сам по себе самостоятелен и реализует заложенный функционал. Но множество частных случаев требуют переопределения всего лишь одного-двух методов. Внимание, вопрос: с точки зрения автора статьи, как обходиться в таких ситуациях?
Не автор, но прокомментирую :-)
Часто забывают, что ООП — это от слова "объект", а не от слова "класс". Задача программиста — сделать так, чтобы получались объекты, обладающие требуемым поведением. В среднестатистическом мейнстримном языке типа C#/Java есть 2 подхода:
Если мне нужно новое поведение, я наследую существующий класс и переопределяю какой-то аспект его поведения.
Если мне нужно новое поведение, я немного иначе "строю" объект, поведение которого мне нужно изменить.
Если весь код написан таким образом, что единственный способ сконструировать объект это new ЧтоТоТам(), конечно тут кроме наследования ЧтоТоТам нет вариантов. Но можно же изначально заложиться на new ЧтоТоТам(new КакЯДелаюВотЭто(), new ИЕщёКакЯДелаюВотЭто()). В таком случае получается на порядок больше мелких классов, где одни классы описывают какой-то конкретный аспект поведения, а другие — просто скручивают несколько таких аспектов поведения в один "настроенный объект". Статья автора, если я правильно понял, про такой подход.
А это общее правило параноика для любого ЯП (хотя больше всего его любят в C++): если вы до конца не определились как вам оформить класс, то делайте его приватным, делайте его конструктор приватным, делайте все методы приватными и невиртуальными, и запрещайте наследование. Чтобы изменить любой из этих пунктов нужна вполне конкретная причина.
Автор, попробуйте пожалуйста разобрать конкретный пример — как сделать редизайн какого-нибудь UI фреймворка, чтобы избежать всепроникающего базового класса Control. Я так понимаю, у вас есть опыт с .NET — предложите вариант принципиальных изменений для Windows Forms или WPF, чтобы там не было иерархий типа https://msdn.microsoft.com/en-us/library/system.windows.controls.button(v=vs.110).aspx :
не можете выразить на языке типов некорректное утверждение
Я и не делал некорректное утверждение — там стоит явная проверка типа. Это вы предложили рассмотреть сценарий, когда в проект приходит полный отморозок и пишет хрень. Язык это позволяет — с этим я ничего поделать не могу. Но работать в итоге оно не будет — и это здорово. И к чему в итоге мы приходим?
Ну, мы же не знаем, что внутри notificationService, и будет ли оно в реальности работать некорректно или нет.
Вы понимаете, что notificationService.notify((ConfirmedEmailAddress)email) падает именно на выражении (ConfirmedEmailAddress)email — т.е. до вызова notify?
Что же хорошего? Есть требование — отправлять уведомления только на подтверждённые email адреса. В вашем случае это требование выполняется только проверкой email.isConfirmed. Если эту проверку забыть, требование будет нарушено, но код продолжит во-первых компилироваться, а во-вторых работать в рантайме. А забыть эту проверку очень легко, потому что она вообще никак не энфорсится. В моём же варианте нужно специально постараться, чтобы обойтись без проверки. Но даже в этом случае вы получите явную ошибку в рантайме.
Не статическую типизацию, а type-driven design, это разные вещи.
Можно пожалуйста вместо красивого "похоронили" написать в чём конкретно проблема?
Когда это "сопротивление" преодолевается одним кастом — люди будут писать каст.
В предыдущем сообщении обратил ваше внимание на то, что даже если обойти компайлтайм "защиту", остаётся ещё рантайм защита — каст заставит код скомпилироваться, но в рантайме оно иногда будет падать. Падать — значительно лучше, чем работать некорректно.
Я писал только о том, что переменные ограниченных и перечислимых типов (не сами эти типы, а имеющие их переменные!) находят гораздо меньше применений на практике, чем этого хотелось бы на первый взгляд.
Эта ветка выросла из вашего заявления про тип EmailAddress:
Тип EmailAddress, предлагаемый в статье, не может существовать, так как адрес валидируется каждым сервером получателя по-своему.
Я всё пытаюсь понять вашу точку зрения по поводу применимости статической типизации — конкретно что не так с типом EmailAddress.
Я ничего не путаю, я пишу о типичном случае применения.
Опишите этот "типичный случай" для начала. Пока вы просто сделали односложное малопонятное утверждение, которое я читаю как "понятие URL само по себе не нужно, нужно понятие Фигня-Которую-Юзер-Написал-В-Адресной-Строке".
Это как если 10 человек будут играть в шахматы против чемпиона мира (по шахматам) путём голосования за каждый ход — что их 10, что 100, что 2 — разницы нет, точно сольют.
Сидеть до ночи изучая F# — это совсем не то же самое, что сидеть до ночи разгребая баги в коде того товарища. Качество вашего времени будет разным. Изучать F# — круто, а фиксить баги — это хрень. Люди вроде вас, автор, как раз в итоге и фиксят баги, и анализируют причины их возникновения, и исходя из этих годами накопленных знаний дают толковый фидбэк на код ревью. Это совершенно нормально — уважать себя, свои знания и опыт, ценить своё время и стараться избегать предсказуемых граблей.
Логика такого масштаба развесистости не имеет права быть реализованной в одном методе. Просто посмотрите на количество "компонентов" — a, b, c, F0, F1, F2, F3, F4, do_something_with(), res, и т.д. Если внутри одного метода действительно такой богатый контекст, это означает, что проблема не в самом методе, а в его окружении. Будет этот метод таким как вы его написали (с новым оператором) или в 3 раза длинее (без нового оператора), разницы совершенно никакой: он хренов бай дизайн.
В этой статье слово "чиллер" и производные от него встречаются не менее 60 раз.
По-моему не работает.
Императивный подход — КАК добиться нужного результата. Например:
button.setBackgroundColor(RED)
(есть эта строчка кода, которая выполняется до каких-то других и после каких-то других; эта строчка — это команда что-то сделать — "установить цвет фона")Декларативный подход — ЧТО собой представляет нужный результат. Например:
<button backgroundColor="red">...
(это не команда — мы не говорим "во-первых я хочу кнопку, а во-вторых — давайте ей установим красный цвет фона"; вместо этого мы описываем результат — кнопка с красным фоном)Ваш пример для декларативного подхода "приготовь яичницу" — это на самом деле императивный подход, т.к. вы описываете не результат, а способ его достижения. Декларативно яичница выглядит вот так: "яичница".
Написать _handleClick таким образом, чтобы он дёргал несколько обработчиков. Вы вопрос неправильно ставите. Обработчик всегда один: нажатие кнопки означает, что юзер "даёт команду" вашему аппликейшну. Как вы там будете эту команду обрабатывать — одним хендлером или десятком — ваше дело. Кнопка со своей задачей справилась — донесла до вас что её нажали.
Вы путаете "логику" и "бизнес-логику". Спрятать кнопку или показать в зависимости от аутентифицированности пользователя — это как раз "логика представления". Будет это if внутри jsx, или это будет непонятная закорючка в handlebars — вообще никакой разницы.
Какая-то длинная статья. Чтобы понять фреймворк это или библиотека, достаточно ответить на вопрос "управляет ли оно потоком выполнения моей программы?". Если да, то это фреймворк, если нет, то это библиотека.
Если вопрос кажется дурацким или непонятным, его можно переформулировать вот так: "в своём коде я отвечаю на вопросы или скорее делаю утверждения?". Первое — использование фреймворка, второе — использование библиотеки.
Я не пропагандирую это "правило параноика" и сам ему не следую. Не это конкретное правило, но правила вообще имеют смысл когда уровень квалификации "среднего" участника команды приводит к регулярным наступаниям на грабли. С точки зрения поставки фич бизнесу всегда проще сказать "всегда делай вот так", чем ждать пока коллега соберёт все грабли, пропустит все дедлайны, научится и станет делать "правильно" осознанно.
Ну не всё так просто:
Посмотрите пожалуйста внимательнее на вашу ссылку, ключевое слово — сквозная функциональность:
В случае моего примера выше мы в явном виде провели декомпозицию.
Я ни в коем случае не говорил, что нужно делать приватные классы по умолчанию. Перечитайте пожалуйста: "если вы до конца не определились как вам оформить класс, то делайте его приватным...". Если вы хотите сделать класс частью интерфейса вашей библиотеки, это просто означает что вы определились.
Я же написал в явном виде — "нужна причина" :-) У вас причина есть, соответственно вы осознанно будете делать классы/методы/конструкторы паблик. Но не у всех есть такая причина.
Это ни в коем случае не АОП. Это самое обычное ООП + делегирование.
Посмотрите примеры использования паттерна "Стратегия" — это собственно делегирование в чистом виде и есть.
Не автор, но прокомментирую :-)
Часто забывают, что ООП — это от слова "объект", а не от слова "класс". Задача программиста — сделать так, чтобы получались объекты, обладающие требуемым поведением. В среднестатистическом мейнстримном языке типа C#/Java есть 2 подхода:
Если весь код написан таким образом, что единственный способ сконструировать объект это
new ЧтоТоТам()
, конечно тут кроме наследования ЧтоТоТам нет вариантов. Но можно же изначально заложиться наnew ЧтоТоТам(new КакЯДелаюВотЭто(), new ИЕщёКакЯДелаюВотЭто())
. В таком случае получается на порядок больше мелких классов, где одни классы описывают какой-то конкретный аспект поведения, а другие — просто скручивают несколько таких аспектов поведения в один "настроенный объект". Статья автора, если я правильно понял, про такой подход.А это общее правило параноика для любого ЯП (хотя больше всего его любят в C++): если вы до конца не определились как вам оформить класс, то делайте его приватным, делайте его конструктор приватным, делайте все методы приватными и невиртуальными, и запрещайте наследование. Чтобы изменить любой из этих пунктов нужна вполне конкретная причина.
Автор, попробуйте пожалуйста разобрать конкретный пример — как сделать редизайн какого-нибудь UI фреймворка, чтобы избежать всепроникающего базового класса Control. Я так понимаю, у вас есть опыт с .NET — предложите вариант принципиальных изменений для Windows Forms или WPF, чтобы там не было иерархий типа https://msdn.microsoft.com/en-us/library/system.windows.controls.button(v=vs.110).aspx :
Я и не делал некорректное утверждение — там стоит явная проверка типа. Это вы предложили рассмотреть сценарий, когда в проект приходит полный отморозок и пишет хрень. Язык это позволяет — с этим я ничего поделать не могу. Но работать в итоге оно не будет — и это здорово. И к чему в итоге мы приходим?
Вы понимаете, что
notificationService.notify((ConfirmedEmailAddress)email)
падает именно на выражении(ConfirmedEmailAddress)email
— т.е. до вызоваnotify
?Что же хорошего? Есть требование — отправлять уведомления только на подтверждённые email адреса. В вашем случае это требование выполняется только проверкой
email.isConfirmed
. Если эту проверку забыть, требование будет нарушено, но код продолжит во-первых компилироваться, а во-вторых работать в рантайме. А забыть эту проверку очень легко, потому что она вообще никак не энфорсится. В моём же варианте нужно специально постараться, чтобы обойтись без проверки. Но даже в этом случае вы получите явную ошибку в рантайме.Можно пожалуйста вместо красивого "похоронили" написать в чём конкретно проблема?
В предыдущем сообщении обратил ваше внимание на то, что даже если обойти компайлтайм "защиту", остаётся ещё рантайм защита — каст заставит код скомпилироваться, но в рантайме оно иногда будет падать. Падать — значительно лучше, чем работать некорректно.
Эта ветка выросла из вашего заявления про тип EmailAddress:
Я всё пытаюсь понять вашу точку зрения по поводу применимости статической типизации — конкретно что не так с типом EmailAddress.
Опишите этот "типичный случай" для начала. Пока вы просто сделали односложное малопонятное утверждение, которое я читаю как "понятие URL само по себе не нужно, нужно понятие Фигня-Которую-Юзер-Написал-В-Адресной-Строке".