-
Почему я против объектов. Часть вторая, техническая
Это продолжение предыдущего поста на тему ООП. Напомню, что пишу его под воздействием книги Егора “Elegant Objects”. Прошлая публикация была немного абстрактной и слегка резковатой. Полагаю, так случается со всеми после чтения какого-либо из текстов Егора. Недаром его идеи будоражат интернет: каждый пост становится предметом бесконечных обсуждений.
В этой части поговорим на более технические темы, поэтому тональность будет поспокойней. Напомню, это не полноценная рецензия на книгу; их уже написано достаточно. Скорее, ниже приведены возражения к некоторым тезисам из “Elegant Objects”.
Сама по себе книга очень понравилась. Хоть я и далек от ООП, но прочел с удовольствием. Польза от материала в том, что он действительно продвигает вперед, заставляет задумываться о смысле обыденных паттернов и приемов. Через всю книгу красной нитью проходит тема поддержки кода, что редко встречается в любой другой литературе на тему разработки.
Выражаясь терминами Ильяхова, в книге описан (и вызывает в читателе) чувственный опыт, то есть все то, что автор пережил и испытал лично. Это делает материал очень убедительным. Возникает особое доверие, которого нет при чтении очередного руководства.
Напомню, Егор предлагает строить программы при помощи неизменяемых объектов. Каждый такой объект представляет интересы реальной и, возможно, меняющейся сущности реального мира. Объект должен запрашивать минимум входных данных, в противном случае он становится композицией других объектов поменьше. С этими же “элегантными” объектами связан отказ от
NULL
,getters/settets
, наследования и других вещей из промышленного ООП, с чем я полностью согласен.В то же время не могу не возразить по следующим тезисам.
В стремлении дробить объекты на мелкие сущности мы неизбежно придем к ситуации, когда их станет очень много. Знания о каждом объекте будут просты, но потребуется знание о том, как их совмещать. Эту информацию можно будет зафиксировать разве что в
readme
илиwiki
к какой-нибудь библиотеке, и большая часть кода будет набираться банальной копи-пастой.Думаю, каждый сталкивался с ситуацией в проекте на Java, когда на каждый квант информации заведен класс, и совершенно не ясно, как построить общую картину. Аргумент выше я позаимствовал из блога Дмитрия Бушенко, который тоже писал отзыв на книгу. Здесь мне нечего добавить к аргументу Дмитрия: помню, разбираясь с Хаскелом, я тоже был поражен взрывному росту типов по мере роста программы. На каждую пару есть функция-конвертор, но пока ее найдешь…
Примеры, которые приводит Егор с классами
Cash
и пр. действительно смотрятся красиво. То же самое можно сказать про классMax
, который инкапсулирует два числа и сам притворяется числом, производя вычисления только когда к нему обращаются как к числу. Сама элегантность! Но все же у меня остались сомнения, что средних размеров проект, выстроенный на маленьких объектах, не начнет пошатываться из-за множества внутренних связей.Рассмотрим код, который соответствует критериям Егора на элегантность. Вот он. Это копирование данных из одного источника данных в другой. Взят из блога Егора.
new LengthOfInput( new TeeInput( new BytesAsInput( new TextAsBytes( new StringAsText( "Hello, world!" ) ) ), new FileAsOutput( new File("/tmp/hello.txt") ) ) ).value(); // happens here
Оставим за скобками вопрос о длине кода (императивная версия займет три строки); нас пока это не интересует.
Главное преимущество подхода Егора выражается в декларативности: мы выстраиваем дерево объектов, которое ничего не делает при создании. Такое дерево можно построить, где-то хранить, передавать куда-то или даже сериализовать. Типичному Lisp-программисту это напомнит принцип “код как данные”, хотя очень условно.
Остается только нажать на крючок, который каскадно спустит весь механизм.
Недостатки метода вытекают из его же достоинств. Прежде всего, дерево лениво. А ленивость не всегда хороша. Чем ленивей структура данных, тем больше будет дистанция между исполнением и ошибкой.
В примере выше все действие совершается в финальном вызове
.value()
верхнего объекта. Сказать, что это неочевидно, значит ничего не сказать. Помню, мне приходилось отлаживать схожий код с отложенными вычислениями. Много сложности вносило то, что при выводе объектов в консоль совершались скрытые действия, например, итерация по коллекции, чтение потоков. Это было очень не очевидно, а потому тяжело в поддержке.Те, кто программирует на Питоне, должны помнить, что с выходом третьей версии все методы, ранее возвращающие списки, теперь стали возвращать ленивые итераторы, по которым можно пройтись один раз. Приходилось каждый такой вызов оборачивать в
list()
, чтобы скопировать элементы в фиксированную коллекцию. Фактически это борьба с ленивостью. Особенно раздражало, когда в интерактивном сеансе выводишь коллекцию в терминал, она считывается целиком, выводится на экран и становится пустой.Словом, я не уверен в достоинствах дерева объектов.
Следующий момент, на который я бы хотел обратить внимание: приглядитесь к именам классов из примера выше. Мне бросается в глаза одинаковый паттерн именования:
ThisAsThat
, то есть Одна сущность в Другую. У экземпляров этих классов всего лишь один метод.value()
(или.text()
,.bytes()
, что угодно), который возвращает результат типа второй сущности.Налицо утилитарность таких объектов: их жизненный цикл состоит исключительно в том, чтобы перевести данные из одного типа в другой. Нужен ли этот класс в дальнейшем? Нет. Это просто преобразование из А в Б. Такой объект даже не заслуживает “уважительного”, как пишет Егор, отношения. Это просто преобразователь, конвертор, читатель, что угодно.
Такие утилитарные объекты нарушают принцип из статьи “перестаньте писать классы”, на которую я ссылался в предыдущей части. Если у класса один метод, то он должен быть функцией. Потому что если мы заинтересованы в одном методе, нет смысла хранить состояние и вообще оборачивать исходные данные в какую-то абстракцию.
Этот принцип кажется мне слишком весомым, чтобы его нарушать.
Взгляните на код: буквально на ровном месте мы возвели множество сущностей:
Text
,Bytes
,Input
,TeaInput
. А ведь всего-то требовалось скопировать данные. С точки зрения программиста, у которого в проекте десяток библиотек и фреймворков, это должна быть автомарная операция.Сейчас я попрошу вас, не перематывая страницу вверх, мысленно построить это же дерево. Что было первым? Пишу по-честному: сначала
LengthOfInput
, под нимTeaInput
, а дельше цепочка из Одного в Другое. То ли байты в текст, то ли строка из текста или наоборот. Кто на ком стоял? Теперь подумайте о том, что копирование данных происходит постоянно и во многих местах. Неужели придется держать порядок в голове?Как я уже писал, появляется потребность в особом знании о том, как организовать такие классы. Напрашивается идея о “схлопывании” такого дерева в отдельный объект-копировальщик, какой-нибудь
DataCopier
, что, конечно, противоречит тезисам Егора. Как тут быть, я не знаю.В третьей части я коснусь темы структур данных и функционального подхода, которым Егор тоже уделил внимание в книге.
-
Ты же понимаешь
Немного найдется речевых оборотов, которые меня раздражают. Я совершенно спокойно отношусь к мату, словам-паразитам, булшит-сленгу – заафектить, консерн, форварднуть, пропоузал.
Даже “я тебя услышал” кажется мне что-то забавным. Но если и есть паттерн, от которого хочется прекратить беседу, так это “ты же понимаешь, что…”. Это ни разу не ок, так говорить.
Подставляя это словосочетание в начало фразы, собеседник пытается навязать мысль, причем весьма грубым способом. Ты же понимаешь, и все, без вариантов. Неважно, что дальнейшее высказывание идет вразрез со всякой логикой, главное сделать упор на первом утверждении.
Этот дешевый трюк применяется исключительно перед публикой. В одиночку он не работает, потому что обоим собеседникам видна вся его ложная предпосылка. Но аудитория редко удерживает в голове всю цепочку, поэтому достаточно огласить взаимное согласие, а затем топить за свое мнение без какой-либо аргументации.
Сюда же относятся “мы все согласны, что…”, “очевидно же, что..” и сто других усилителей:
– Ты же понимаешь, что функциональное программирование применяется только в академической среде.
– Ты же понимаешь, что большой проект невозможен на языке без статической типизации.
– Ты же понимаешь, что серьезный бизнес выбирает .Net.
– Очевидно же, GoLang это технология будущего.
– Очевидно же, что Java умирает.
– Все согласятся с тем, что Кложа – хайп.
– Конечно же, только на Руби веб-разработка максимально эффективна.
Подобные фразочки – верный индикатор того, что разговор пора сворачивать. Или по крайней мере что беседа перешла совсем не в то русло.
Во-первых, некрасиво утверждать что-то за собеседника. Хочешь знать его мнение – спроси. Во-вторых, если вы оба придерживаетесь одних идей по теме, то и спорить не о чем.
Я никогда не стесняюсь ответить, что нет, я не понимаю и мне не очевидно. И обязательно кину ответочку – несколько открытых вопросов. Кому это очевидно? Что дало повод так думать? Откуда эти сведения?
В попытке объяснить оппонент, как правило, сам же запутается в противоречиях. Вызвался – теперь крутись на сковородке. Я уже закрыл чат, а он будет писать еще час.
Лучше вообще не разговаривать с теми, кто злоупотребляет словами-усилителями. Мне приходилось общаться с разработчиком, который, если наверняка знал, что прав, добавлял пафосное “Коне-е-ечно!” к каждой фразе. Это было ужасно: малейший диалог с ним звучал как разговор короля с подданным:
– Можно пофиксить первым способом, а можно вторым. Предлагаю вторым. Что скажешь?
– Вторым коне-е-ечно!
Типа, я уже все продумал, а ты говно.
Вы же понимаете, что так делать не нужно. Верно?
-
Weekly links #31
-
Joel hasn’t been blogging for years, but thank him, a new post has been published.
-
GitHub’s technology predictions for 2018
I’m not a fan of making and reading predictions, but this one deserves our attention.
-
Five ways to paginate in Postgres
Really, five ways to do that, from the standard one to the most exotic I’ve ever seen. Quite interesting article!
-
Capturing ClojureScript Errors on the Server
Good technical stuff from Dmitry on Clojure.
-
-
Don't use Leiningen to run shell-scripts
Working with various Clojure projects, I noticed one thing that really worries me. I’m talking about developers who add more and more entries into
:prep-tasks
vector inproject.clj
file. Please stop it. Every time you want to put there a new task to compile CSS or build a Docker image, take a minute or two to think on that. Let’s discuss the problem.Lein is a great tool of course, really a piece of art. But its abilities are not unlimited. Remember, its main purpose is to manage a Clojure-driven project. I think, everybody agree with that statement.
The Clojure project you are working on is only a part of a top-level project in business terms. Besides both server and UI sides, your application probably sends emails, pushes notifications, interacts with Blockchain network and does further more things.
According to GitHub statistics, you may have even 99% percent of code written with Clojure. But still, there is definitely something that is beyond it. Thus, please do not use Lein for those tasks that do no have any relation to Clojure code.
Recently, I faced a situation when running REPL caused building Ethereum smart contracts first. That step assumes you have installed software of proper versions and configured paths, text configs, etc. Compiling them was a really resource- and time-consuming duty.
The next step was to compile CSS sources. Again, it required installing Ruby, less and wait for some time.
Remember, I did’n want all of this to happen. All I wanted to do is to connect to REPL from Emacs and debug one tiny function. Needless to say, I just commented those tasks in project file.
Again: your Clojure project should know nothing about compiling CSS, building email templates, fetching anything from the network, querying Ethereum, building Docker image or whatever else. Especially when we talking about running REPL. It should run without any troubles at any time.
That’s why I’m strictly against using
lein-shell
plugin or something similar that lets you run shell commands from Lein.Of course, building a project requires passing through a list of particular steps that were partially mentioned above. What should we use for that? Shell-scripts? Any modern Javascript task runner? No, just Make utility that has been here for ages.
“Put that gun down and let me explain.” (c)
I know Make is an ancient tool that came from rough C/C++ times. Those days, developers knew quite few about a pipeline, CI or methodologies in general. Almost every single programming language nowadays offers a task runner that takes modern requirements into account. But still, for a set of tasks that might be run upon your project, there is nothing better then a Makefile at the top of file structure. And here is why.
Make utility is a binary tool that does not force you to install a new version of Node.js, Python or Ruby. Probably, it’s already installed on your computer since most of Linux distributions have it out from the box. It works perfectly of various systems. I have never faced any troubles with it switching between Linux and Mac.
Such modern shells as zsh support auto-completing make targets when pressing
<TAB>
character after “make”. That really saves time especially when you use prefixes. Say, in my pet project, besides Clojure-related targets, I’ve got a bunch of commands to operate on Docker images. These aredocker-run
,docker-build
,docker-compose-up/down
and more. Each of them takes 60 to 100 characters so it’s impossible to remember them. So in terminal, I typemake doc<TAB>
and see a short subset of Docker-related stuff.I consider any Makefile as not just a list of commands but rather a knowledge base of your project. The more the project develops, the more operations you need to perform over it. Where to store all the those commands? In your wiki? Nobody reads it. A better solution would be to keep them as close to the code as it possible.
Make utility runs extremely fast whereas lein needs about five seconds to boot up. That’s pretty long time, really. Imagine each command line tool hangs for a couple of seconds before it runs. That would be a hell on your computer. A situation that makes me angry is when I mistype in a long lein command like
lein migratus craete user-updated
, wait for five seconds and see an error message saying there is nocraete
subcommand in migratus. With make utility, there is no an option for such things.One of my favorite features is to ask a user for prompt when typing names, passwords or any other sensitive data. Here is an example of how usually I create a new SQL migration:
create-migration: @read -p "Enter migration name: " migration \ && lein migratus create $$migration
When I type
make crea<TAB><RET>
(remember, auto-complete magic works here), the system asks for a new migration name. I enter a string that I need and a new migration named properly appears.Makefiles may include other ones so probably you can maintain separate files for both production/developer modes or for developers/ops teams. Since those files support comments, you are welcome to put doc hints there.
The utility allows to chain targets. When I start working on a new task, at least I need to perform three steps: 1) migrate the database; 2) run tests to ensure everything works before I change something; 3) launch REPL. In my make file, I’ve got a separate target for each step. But I can call them in chain as well:
make mig<TAB> te<TAB> re<TAB>
that expands into:
make migrate test repl
and finally becomes:
lein migratus migrate lein test lein repl
Inside a makefile, you can call for another makefile what may become a powerful feature of your build process. Here is how it works.
Say, in our project, on the top level of it there is a “email” folder that brings Node.js project for building email templates. Our Clojure application use those compiled templates for further processing with Selmer to send final emails to our clients. It’s obvious, I need those templates only when I work with our email subsystem and never else. So it would be madness to force all our developers to install Node.js with tons of packages and compile the templates each time they launch REPL.
Inside “email” folder, there is a separate Makefile that knows how to deal with that sub-project. It installs all the requirements and has some useful targets, say, to run compiler in debug mode or open a browser window for preview.
A small fragment of that make file:
all: install build dist install: yarn install build: node_modules/.bin/gulp build --production dist: cp ...
The default target performs all the targets that are required to get final templates. Now, in the main make file that is on the root of the project, I put:
EMAILDIR := $(CURDIR)/email .PHONY: email email: make -C $(EMAILDIR) uberjar-build: email lein uberjar
So this configuration gives me freedom of choice. When I launch REPL, I don’t need all that stuff to deal with compiling emails. Probably, I may work in a company for years without touching them. But those guys who do, they run
make email
and get the full and ready email installation. Finally, no one be able to build an Uberjar without compiling fresh emails.Now take into account that those emails were just a small part of a project. Remember, for successful production build you might need fetching huge JSON declarations from 3rd-party services; compiling smart contracts; building Docker images; building CSS and much more. Now answer, whey
lein
utility that even doesn’t have any default capabilities for that, should do it instead of special tools designed for exactly those things?I think it’s obvious now that
lein
only should be used to manage your Clojure code. Never configure it to run non-Clojure-related stuff. -
Как ivi.ru крадет деньги
Месяц назад решили с женой посмотреть один кинчик. Я уже не в том возрасте, чтобы шариться по торрент-помойкам, поэтому покупаю на Ютубе. Но именно этого фильма там не было. Гугл вывел меня на ivi.ru, где фильм был, и вот что из этого вышло.
Купил, посмотрели. Через месяц приходит письмо, что с карты списаны 399 рублей за продление ежемесячной подписки. Полез на сайт и выяснил, что:
-
эти черти сохранили мою карту, хотя нигде подобной галочки не было;
-
они же оформили мне платную подписку, хотя, опять же, нигде этих галочек не было. Вот так, купив фильм у ivi, попадаешь на бабки.
А дальше еще интересней. Звоню в поддержку. Заранее продумал разговор. Сотрудник, не спросив ни моего имени, ни емейла, словом, никак не подтвердив мою личность, отвечает – назовите номер карты, сделаю возврат. Я назвал, он на кнопку клац – отвечает, готово, ждите денег. И действительно, на следующий день они пришли.
Напрашиваются несколько выводов. Первый – ivi.ru действует незаконно, сохраняя карты и подключая платные услуги без ведома владельца. Что-то вроде мобильного опсоса, потерявшего берега.
Второе – в команде ivi об этом прекрасно знают, и уже отработан стандартный сценарий возврата денег. Моментальная реакция говорит о том, что на этот случай заведена отдельная кнопка. Никаких личных данных и подтверждений личности не просят, чтобы не раздувать скандал. Вы когда-нибудь делали возврат в обычном магазине? Там и заявление, и паспорт потребуют. А тут анонимно и по номеру карты.
Третье – действия ivi.ru это обычная кража. Конечно, они прислали письмо. Но что если я не читаю почту? Упор сделан на то, что я не замечу списания. Типичный русский наебизнес. Почему-то именно русская компания смошенничала с моей картой. Совпадение? Не думаю (с).
Короче, ivi.ru, обманщики вы и воры. Позорище.
-
-
Почему я против объектов. Часть первая, философская
UPD: вторая часть.
LT;DR: это очень личный взгляд на ООП, который я обдумывал довольно долго, но окончательно сформировал после прочтения книги Егора “Elegant Objects”. Если коротко, я высказываюсь против идеи представления кода в объектной модели. Некоторые аргументы почерпнуты из сторонних публикаций и адаптированы для краткости. В таких случаях я даю ссылку на оригинал.
Изначально я планировал написать два в одном: и про ментальную, и про техническую составляющие ООП, но так как текст получается объемный, опубликую пока что первую часть о том, как ООП ложится на (мой) мозг.
Объекты, как утверждают Википедия и учебники, помогают отобразить картину физического мира в коде. Думаю, всем это объяснял преподаватель в школе или университете на примере класса кота или собаки. Потом примеры с наследованием, переопределением класса “голос” и так далее.
За последние годы мое увлечение ООП плавно сошло на нет. В одном из постов я прямо признавался, что не понимаю его принципов. Главное, что меня смущает: во время работы, если это был не строго объектный язык, а Питон, JS или PHP, то все задачи я решал простыми функциями. Каждый раз мне говорили, что просто проект легкий, что однажды наступит БОЛЬШОЙ ПРОЕКТ, где с функциями ты хлебнешь. Но время шло, БОЛЬШОЙ ПРОЕКТ так и не наступил, и, кажется, в эпоху микро-сервисов его уже не дождешься. А я все пишу на функциях и неизменяемых коллекциях. В чем же дело?
И любой объектный код я переписывал на функциях с сокращением числа строк до двух раз. Как так?
Причина мне видится в том, что ООП очень абстрактно. Одно и то же событие реального мира можно интерпретировать в объектной модели совершенно по-разному. Отсюда и трудности изучения, и лишний код.
Представим, что Вася отправляет письмо Пете. На языке объектов это записывается так (опустим определения классов):
User vasya = new User("Vasya"); User petya = new User("Petya"); Message msg = new Message("Hello!"); vasya.send(msg, petya);
Тут, возможно, я нарушил один из бесчисленных паттернов ООП: отвечать за отправку должен не пользователь, а само сообщение (что весьма спорно). Поэтому следующий после меня разработчик поправит код вот так:
msg.send(from=vasya, to=petya)
Это уже не синтаксис Джавы, в ней нет именованных аргументов, а скорее Питона. Написал так, чтоб было понятно: метод
.send()
принимает отправителя и получателя и сам отправляет письмо.Уже на этом этапе видна вся неоднозначность и зыбкость объектного мышления. Согласно паттернам, отправку письма логичней вынести в класс сообщения. С другой стороны, в реальном мире именно пользователь инициирует отправку. Письмо это неодушевленная сущность, набор байт или листок бумаги. Как он может что-то отправлять?
Теперь представим, что на каждый чих мы проектируем класс и создаем объект. Как можно утверждать, что отношения между ними логически верны? Как это доказать?
Зыбкость этого принципа порождает все новые и новые книги и статьи в блогах, где авторы доказывают, что именно их подход обеспечивает прозрачность и поддерживаемость программы, написанной в объектном стиле. Но на более приземленном уровне это выражается, простите, в срачи – бурные обсуждения в чатах, когда эмоции и мнения намного опережают опыт.
Проблему отношений между объектами я называю “кто на ком стоял”. В самом деле, ручка пишет по бумаге
pen.writeOn(paper)
или бумага с помощью ручкиpaper.write(pen)
? Каждый, кто поспешит ответом в духе “ну конечно первый (второй) вариант”, не учитывает, что это совершенно субъективно.Предполагаю, что именно поэтому в ООП-среде так популярен рефакторинг. Ключевая фича каждой промышленной IDE – облегчение рефакторинга: переименование и перенос методов, автоматическая адаптация кода.
В окружающем нас реальном мире физические объекты играют далеко не решающую роль. Иными словами, не все можно выразить через объекты. Рассмотрим, например, акт рукопожатия. Следуя принципам Егора, отразить в коде это можно так:
class User { private String name User(String name) { this.name = name; } } class Handshake { User user1; User user2; Handshare(User user1, User user2) { this.user1 = user1; this.user2 = user2; } void shake() { // ... лог в консоль или что угодно } } User user1 = new User("Ivan"); User user2 = new User("Petr"); Handshake hs = new Handshake(user1, user2); hs.shake();
Но ведь физически рукопожатия не существует. Нет в нашем мире такого объекта, его нельзя купить, смастерить, поставить на полку. Это событие, акт или, выражаясь точнее, действие! А что в программировании выражает действие? Функция.
Предположим теперь, что друзья пожали руки несколько раз с интервалом в 10 минут. Будет ли правильным вызывать у того же объекта метод
.shake()
? Или на каждый раз создавать новый объект?Если первое, не противоречит ли это принципам ООП? Ведь все это разные рукопожатия, совершенно независящие друг от друга. Что если они жали руки с перерывом в 10 лет?
Если второе, то чем это отличается от вызова функции? Мы создаем объект, вызываем единственный метод и тут же забываем его? Зачем тогда класс и объект? Если один-два метода это все, что нам нужно от класса, не проще ли завести функцию?
На эту тему уже неписана отличная статья “перестаньте писать классы”, изучите обязательно. Видео со слайдами, перевод на Хабре.
Далее можно рассуждать о более сложных вещах. Рукопожатие все же имеет косвенное отношение к физическим объектам. Но такие абстрактные понятия как ненависть, симпатия, эгоизм, честность, религия выразить объектно невозможно. Я имею в виду, что не составит труда написать класс с нужным именем, который инкапсулирует другие классы. Но спрашивается, в чем здесь смысл?
Добавьте процессы: горение, магнитные волны, свет. Все, что перечислено выше имеет значение только в действии. Важен не сам объект, а как он изменяет другие сущности. Опять же, про написана хорошая статья, где рассматривается река с точки зрения ООП- и ФП-программиста.
В книге “Elegant Objects” Егор справедливо замечает, что сегодняшнее ООП это просто структура данных с прикрепленными к ней функциями. Однако, не делается акцент на том, сколь масштабно это явление. С выходом языков Go и Rust на них перешли тысячи бывших Java и С++ разработчиков, и, похоже, сочли новые языки вполне себе объектными. А ведь и в Go и в Rust объекты – это банальные сишные структуры. И если функция принимает первым аргументом такую структуру, то вместо
some_action(data, 1, "test")
можно написатьdata.some_action(1, "test")
. Вот и вся разница.Очевидно, сегодня для большинства вызов функции через точку кажется главным показателем объектности языка. Я не хочу никого этим обидеть. Человеку свойственно упрощать рутину: выражать код всей программы с помощью настоящих, “элегантных” объектов, как советует Егор, мне кажется неподъемным делом. А со структурам и функциями проще, и даже похоже на ООП.
Я утверждаю, что современный ООП-подход, согласно которому мы выражаем все сущности как объекты, так же похож на окружающий мир, как шахматы на реальную войну. То есть в кукольной манере и гротескным упрощением. Тысячи лет назад войска действительно выстраивались напротив противника, впереди пешие войска, по бокам боевые слоны, сзади командование. Сегодня это не так.
Кратко резюмируя. В ООП до сих пор нет четкого понимания, как выстраивать отношения между объектами. Огромное число паттернов сбивает с толку. Объекты в коде не могут точно описать нематериальные понятия: природные процессы, отношения между людьми. Гораздо большее значение имеет не объект, а действие над ним.
В следующий раз поговорим о технической стороне ООП.
-
Надо короче
Удивительно, что в детских организациях, например, образовательных и спортивных школах совершенно не знают, как проводить мероприятия. Я имею в виду провести так, чтобы дети не устали от бессмысленного ожидания.
Типичную линейку в школе ставят следующим образом. Выходит директор. Выходит завуч. Выходит сука-единорос-депутат (иногда – его представитель). Выходит ветеран. Выходит сраный министр культуры и еще кто-то в этом же роде.
Каждый из них несет полную пургу: хочет что-то пожелать, пытается пошутить. Бедные дети стоят и слушают эту ахинею. Сорок минут, час. Потом еще сценка. Неужели нельзя уложиться в 30 минут? Выпиздить метлой этих балаболов и просто запустить детей в класс?
Всем учителям известно, как трудно удержать внимание ребенка хотя бы из-за физиологических причин. Урок подготовишки длится 25 минут, первоклассника – 30 минут, и так далее с шагом в 5 минут. Только к третьему классу урок начинает длиться стандартный академический час. Так почему же вы, учителя-завучи-директор, из года в год закатываете линейку на час с лишним?
Соревнования в спортивных школах ничуть не лучше. Опять же парадокс – каждый тренер должен уметь организовать детей. Заставить слушаться группу из 15 человек это особый навык. Но правильно организовать соревнования никто не умеет.
Зашли в зал и ждут. Участники на месте, судьи на месте, родители расселись. Нет, сука, чего-то ждут. Слово директору школы. Слово организатору. Гимн России. И только потом, с постоянными задержками, поломками микрофона, потерей нужных бумажек, начинаются выступления детей.
Родители уже не ждут результатов, потому что измучены больше детей. Мест на всех нет, гардероба хватило на половину пришедших. Только выступил ребенок, в машину и домой.
А детские концерты! Выступающим по 6-8 лет, концерт длится 1 час 20 минут! Те, кто в конце, к моменту выступления становятся как мочалки. Организаторы издеваются над родителями: каждый же пришел только ради своего ребенка. Почему я должен сначала высидеть 15 номеров? Почему нельзя провести два концерта?
Та же история с мультфильмами и представлениями. Почему нельзя поставить пьесу на 45 минут вместо полутора часов? Ну или час максимум. Это же долго! Все сцены очевидно затянуты. Ребенок теряет суть повествования. Главные идеи в детских произведениях просты, их невозможно растянуть на час без очевидных искажений.
Откуда эта манера впихнуть в детей как можно больше? Наоборот, даже с точки зрения бизнеса короткие постановки выгодней: ребенок (и родитель) меньше устанет, значит, останется больше потенциала для потребления других услуг.
Тем, у кого есть дети, предлагаю эксперимент. Попросите пересказать вашего ребенка полуторачасовой мультфильм, который он только что посмотрел. Как зовут персонажей, в чем их конфликт, куда они пошли\полетели, что сделали, чем закончилось. Редкий ребенок ответит на все вопросы. Даже вы не сможете пересказать целый фильм, он остался в голове в виде отдельных образов и сцен.
Лично мне с возрастом все больше и больше книг и фильмов кажутся затянутыми. Короче надо, короче! Техническую литературу невозможно читать: одну и ту же мысль обсасывают на 10 страницах. Предложения порой столь похожи, что напоминают копипасту. Актеры в фильмах начинают конфликт из-за ничего, проблемы надуманны, сцены длятся дольше, чем нужно. Вертится мысль – а не пора бы вам свалить уже из кадра или занять зрителя чем-то поинтересней?
Хронометраж, группа выступающих, число слайдов в презентации, пункты в повестке дня – все это лучше укоротить. Всегда.
-
Итоги 2017 года
Упомнить все невозможно, поэтому пробежался по блогу и выделил следующее.
Стал больше писать в блог. Написал 99 постов не считая этого. Значит, всего 100. Красиво.
Повысил долю английского в блоге. Где-то каждый пятый пост на английском. Писать на нем медленней; порой трудно передать мысль в нужном ключе.
В этом году блогу стукнуло 5 лет – юбилей!
Изнемогая в Турции, опубликовал подробное руководство по переходу с PostgreSQL на Datomic. Статья выстрелила: ее разместили на кложурных ресурсах и Hacker News. В News. В течение недели в блог заходили тысячи человек из англоязычной аудитории.
Публиковался в IndieHackers с рассказом про Queryfeed.
Завел Телеграм-канал в параллель блогу.
Весь год проводил митапы в нашем клубе Рефакторинга. Видосы – на нашем канале и сайте.
Стал читать про криптовалюты. Книга Mastering Bitcoin – хороша, но больно занудна.
Путешествовал! Ездил в Берлин на Евро-кложу и в Балтимор на юбилейную конфу. В первый раз в жизни побывал в США. По ссылкам – отчеты о городах.
Стримил Кложу, в т.ч. и на английском. К сожалению, так и не сделал это занятие регулярным.
Участвовал в Хайлоад-Капе! Ничего не занял, но был дикий драйв. Выгородил Кложу с ее Датомиком. Отчитался на английском.
Написал и поддерживаю свою первую библиотеку на Кложе, которой действительно пользуются.
Вместо с Никитой и Рахимом стал писать в блог Grumpy – это где жалуются на плохой дизайн. Изливаю там душу.
В этом году организм сказал твердое нет долгой работе за компом: заболели шея и руки. Как выкрутился расскажу в следующем году.
Уволился из проекта. Осталась неделя в январе, но это мелочи.
Поставил самую большую в жизни елку.
С праздничком!
-
Документы
Перевожу Гугло-документы в обычный офис. Купил у Микрософта офисный пакет на год, не нарадуюсь. Работает очень быстро. Файлы открываются мгновенно, как-никак нативный C++ вместо Джаваскрипта.
Конечно, офисный редактор на Джаваскрипте это целое искусство, но меня как пользователя это не должно волновать. Правда в том, что в нативном офисе можно открыть 10 документов и спокойно переключаться по ним в течение дня.
В Гугловом офисе каждая вкладка живет в изолированном пространстве. Хром в произвольном порядке выгружает вкладки на диск. Казалось, 10 минут назад ты был в этом документе, переключаешься – и 5 секунд ждешь, пока вкладка подгрузится.
Документ чуть больший пары абзацев прогружается с ощутимыми тормозами. Не дай бог там еще комментарии или картинки
Интерфейс Гугло-дравйва крайне убог и тоже тормозит. Создать в нем папку и скопировать файлы это целая история. Или ставь клиент Драйва для синхронизации с локальной папкой, но там тоже полная жесть.
Случаются странные баги, например, пропадают содержания картинок. Если их подвигать на пиксель влево-вправо, они опять появляются.
Неоценимый плюс Гугла состоит в шаринге и доступности таких онлайн-документов. Пошарить какую-то табличку или бриф между командой теперь проще простого. Но и у Микрософта есть какое-то там облачное хранилище, которое я еще не смотрел.
Личные документы (особенно финансовые) следует хранить в нативном формате. Потому что порой ищешь данные за прошлый год и нужно БЫСТРО открыть 10 документов. В Хроме это просто невозможно.
Короче, я не против веб-приложений, которые косят под нативчик, тех же Слаки или Атома, например. Можно поиграться с ними, если время отклика не поджимает. Скажем, скорость доставки сообщений в Слаке мне вообще безразлична. А вот работа с документами это диаметрально другое. Оно стоит годовой подписки на нативную программу.
-
Егор о поездке в Воронеж
The meetup was really good. I would say, it was one of the best events I’ve took participation in. People were very into the topics, had a lot of questions, were aggressive and friendly enough) They paid for the entire travel, we had lunch, dinner, everything. A+ experience!
Так-то!