А ты посмотри в сторону ASP.NET MVC. Или в сторону какого-нибудь Spring Framework. Откроешь для себя массу нового:
1. У нас нету координаты лошади. У нас есть объект «лошадь», которая как-то там грузится из некоего репозитория в persistence layer'е, а описывается этот момент XML-файлом, но алгоритм совершенно не зависит от…
2. А если у нас не PHP, то никто не мешает лошадке вообще жить в памяти, в зависимости от. И никуда она не денется, ибо AppDomain/тред с лошадкой будет жить, пока запущен веб-сервер
3. Вообще весь сайт может быть сделан как набор взаимодействующих друг с другом объектов (а вовсе не веб-страниц), которые как-то там маппятся на рельные view, описанные шаблонами.
4. Это всё отжирает память, согласен. Но уж лучше пусть хранится лишних два объекта, чем на каждый int уходит по 128 байт вместо 4 (да-да, это же PHP, глобальный и надёжный).
5. И лучше уж пусть отжирается память, чем всё время дёргается БД. Я, например, переписав чат на одном местном сайте на Java, получил огромный прирост производительности. Да, в PHP-ной версии использовался memcache. Но всё это кажется такими костылями, когда есть возможность держать очередь сообщений в памяти и отдавать ответы на запросы прямо из неё, вообще не делая новые коннекты к базе. Хотя какие нафиг коннекты? sessionFactory.openSession(). И ни о каких БД я не знаю!
6. В результате я получаю код, в который проще, в разы проще вносить изменения. Вот увидел я, что некие персистентные сущности тормозят работу сайта. Я просто настрою кэширование в Hibernate/Entity Framework. А в PHP со структурным подходом мне придётся мало того, что добавить кэширование данных при их запросе, так ещё и вставлять убийство «прокисшего» кэша во всех местах, где эти сущности как-то меняются.
7. Конечно, в PHP можно абстрагироваться, выделить слой работы с данными, слой представления. Но тогда мы либо используем средства ООП в PHP, либо, упрямствуя, получим точь-в-точь ООП как GTK+, без поддержки со стороны языка. И, кстати, оно будет тормозить систему. Ибо тот же ORM надо будет поднимать не при старте сервера, а при _каждом_ запросе. И, замечу, всяческие акселераторы не спасают.
… что идеально подходило для хомячкомых домашних страничек с часами и гостевой книгой. Вот только «СУБД» без транзакций ни для чего серьёзного не годится, а за приложения без транзакций надо руки отрывать. Потом, когда эпоха домашних страничек прошла, MySQL начал потихоньку развиваться в правильном направлении. Вот только у PG к тому времени уже всё нужное было, и MySQL вышел за счёт привычки. Отставание MySQL очевидно и поныне.
Насчёт PostgreSQL. На SQL-рельсы он начал переезжать примерно во времена зарождения MySQL. А относительной стабильности добился и того позже.
Да что там последний год? Слон гораздо лучше MySQL. Единственное, почему MySQL оказался популярнее — он был раньше. Пока слон развернулся и более-менее подчистил баги, MySQL уже был облюбован хомячками для своих хоумпаг.
простите, случайно Enter нажался
Так вот, даже на таких «выразительных» языках объём кода в серьёзных проектах по-прежнему остаётся очень и очень немаленьким. Да, профессионал способен написать более выразительный код, но это отнюдь не означает, что объём кода стремится к нулю.
Ну а тезисом по поводу фреймворков ты меня даже позабавил. Ибо я сейчас к нему добавлю тезис «компилятор», и его смысл ничуть не изменится. А на самом деле «все эти фреймворки и библиотеки» нужны для того, чтобы не решать повторно уже решённые задачи. Прямо так и вижу, как труъ-профи пишет парсер XML вручную, при этом объём кода исчезающе мал…
По поводу IDE уже написал. IDE нужен ровно для того, чтобы увеличивать производительность программиста. И профессионал не будет писать в блокноте. Профессионал, во-первых, умеет грамотно пользоваться всеми фичами используемой IDE, а во-вторых, пишет код, который хорошо поддерживался бы со стороны IDE, т.е. свёл бы к минимуму использование всяческих кодогенераций, явных приведений типа в статически типизированных языках (хотя, тут даже не IDE, а компилятор), строкового описания различных сущностей в программе и т.п. и пользовался бы существующими механизмами языка.
Пустая демагогия. Объём кода не снижается за счёт увеличения «профессионализма». Объём кода уменьшается за счёт
1) увеличения выразительности языка
2) использования готовых фреймворков и библиотек
тем не менее, даже на «выразительных» языках вроде Python, Ruby или
А четвёртая стадия — это осознание того, что на второй стадии программист взял за основу правильные идеи, но выстроил на их базе карго-культ. Думаете, зачем нужны все эти паттерны и слои абстракции? Ради красивости? Красота как раз в простоте и лаконичности. А нужны они как раз ради того, чтобы повысить гибкость этой системы. Если же результат оказывался совершенно противоположным, это может означать только то, что паттерны применялись неправильно и использовались неверные абстракции. И, кстати, как я понял, на второй стадии получались большие методы (в противовес третьей стадии). А это ну никак нельзя назвать «красивым кодом»…
И ещё про кодогенерацию. В теории оно конечно хорошо. А вот на практике… Из чего генерится код? Из XML? Из собственного языка? В первом случае ещё можно худо-бедно подсунуть IDE схему. Во втором — придётся писать «в блокноте». А поддержка со стороны IDE в разы повышает скорость кодирования, и на порядки — скорость внесения изменений.
Мне вот интересно, неужели происходит попиксельная обработка изображения с помощью JS (а не используется какой-нибудь Canvas)? Тогда я вообще удивлён, что JS настолько быстр, что в течение секунды успевает полностью обработать картинку. Интересно, где можно посмотреть на примеры того, как JS попиксельно обрабатывает картинку?
Буду категоричным. Несмотря на то что, да, на меня после этого свалится куча минусов. Статья ужасна.
Если-бы я поставил $Result после проверки !$queryResource, то глазами пришлось-бы бегло перечитывать код в поисках типа этой переменной.
Совсем непонятно, почему. Лично я думаю, что объяснение здесь высосано из пальца. Впрочем, объяснение в исходной статье тоже хромает, но она хотя бы есть.
Поэтому, хороший программист первым делом избавится от излишнего кода, проделав простейший Рефакторинг каждого метода (особенно, когда они занимают 300 строчек), разбив его на более легкие методы и функции с ясными очевидными названиями.
А вот как показывает моя практика, не всегда это возможно делать. Да, я и сам стараюсь делать методы максимально компактными. Но иногда не выходит. Тут надо понимать, что нагородив методов, мы перейдём от спагетти в одном методе к спагетти из кучи непонятных private-методов. Да и не всегда методам удаётся дать внятное название, вот и получается набор методов вида DoMysteryousThing и DoSomeOtherMysteriousThing. Конечно, к ним можно подписать комментарии. Но! Почему бы тогда не написать всё в одном методе, разделив его этими самыми комментариями на логические блоки?
Возврат результата функции через ее параметр
А вот тут автор вообще приводит абсурдный пример. Нехорошая это практика возвращать массив или bool. Тем более, это не прокатит в статически типизированных языках. А следующий пример — это возврат к тёмным временам C (поморщился). Вообще, если так уж нужно избежать выбрасывания исключения, то идеальное решение — вернуть структуру вроде Error, конкретизированную типом результата метода.
Полностью классы я не привожу. Но уже становится ясным, по каким причинам стоит задуматься о том, каким методом пользоваться для изменения переменной. При всём при том, стоит задуматься о том, как ваш класс может использовать в многопоточных приложениях, или например, при работе с БД.
Мне не становится ясным ничуть. Вообще, своё мнение об уместности использования свойств я оставил в развёрнутом комментарии к исходной статье.
В функциональном программировании по хорошему счёту — разделяют на «атомарные» функции предварительной инициализации. Например — это реализовано в OpenGL. Но я приведу пример автора:
Вопрос к автору? А вы вообще осознаёте, что такое «функциональное программирование»? И при чём тогда OpenGL и «инициализация»?
А теперь о минусах подхода автора. Почему не стоит делать, как он говорит (в языках без поддержки именованных параметров):
Из приведённого ниже фрагмента кода вообще не ясно, почему не стоит делать, как он говорит. И, кстати, я бы этот код написал как-то так:
Да, как видите, не очень удобно. Зато не надо запоминать в какой последовательности надо передавать параметры. Хотя да, IDE зачастую в помощь. А вот читается код всё равно гораздо лучше. Разумеется, всё было бы ещё лучше, если бы были именованные параметры. Да, надо заметить, что обычно рисование происходит через некий «контекст», который хранится либо внутри самого объекта, через который производится рисование, либо передаётся параметром. Обычно этот контекст и содержит информацию о кистях, шрифтах, преобразованиях. Собственно, это то, про что автор хотел сказать в случае с OpenGL. Вот только к функциональному программированию это никак не относится. И с ООП, кстати, вполне себе уживается.
Дело не в функции. Посмотри, как сделано на C#. Select и Where не являются методами интерфейса IEnumerable. Они объявлены в статическом классе Enumerable. Благодаря extension methods можно записывать так, как я показал, а не что-то вроде:
return Enumerable.Where(Enumerable.Select(sequence, x => x * x), x => x % 2 == 1);
Это в некотором смысле аналог пункта 4 статьи.
Аналогично, в JS можно добавить методы map и filter в Array.prototype и так же пользоваться себе наздоровье точечной нотацией без увеличения уровня вложенности и дублирования названия helper-класса.
Haskell тут вообще не совсем уместен, так как он не ООП. Но вот и в нём есть средство (в виде операции $), которая позволяет избегать лишних скобок и записывать производимые операции последовательно, а не внутри друг друга.
Не совсем. Во-первых, monkey patching относится только к динамическим языкам. Я же привёл в примере C#, который статически типизирован (и решает проблему с помощью extension methods). Во-вторых, я говорил не про изменение кода, а лишь про наполнение существующего класса (или прототипа) новыми «общими» функциями. Так, например, гораздо изящнее реализуется то, что зовётся «обобщённым программированием».
Хорошая статья, спасибо. Однако, как мне показалось, люди начали совершенно необоснованно ругаться на пункт 7. Видимо, просто не прочли про то, что следует использовать встроенный в язык механизм свойств, а не выпячивать наружу публичные поля класса. Кстати, это возможно делать и в PHP :-) Я считаю, что в данном вопросе нужно всё-таки не мыслить догмами. Скорее, свойства стоит использовать, если и геттер и сеттер производят небольшие действия. Если же это что-то, что работает очень долго и включает нетривиальную логику, то следует использовать методы. Кстати, ещё один случай, когда стоит предпочесть метод — это отсутствие самоочевидной семантики свойств. Например, если такой код:
if (foo.bar == 1)
foo.bar = 1;
что-то делает (ну, разве что кроме, например, ленивой загрузки, кэширования и т.п.), то bar — никакое не свойство.
И, кстати, раз уж статья рассказывает о несовершенстве языков, приводящем к «некрасивому коду», то могу напомнить ещё один недостаток. Касается он отсутствия возможности писать расширения к существующим классам. Выливается это в то, что приходится писать всевозможные helper-классы и напрямую писать что-то вроде:
Helper.ext(obj, foo, bar)
вместо
obj.ext(foo, bar)
В качестве «реальной» задачи для иллюстрации данного замечания могу предложить следующий пример. Дана последовательность целых чисел. Требуется найти квадраты нечётных чисел. Сравните пример на Haskell:
foo = map (**2) $ filter odd
и на C# 3.0
public static IEnumerable<int> Foo(IEnumerable<int> sequence)
{
return sequence.Where(x => x % 2 == 1).Select(x => x * x);
}
с примером на Python
def foo(sequence):
return map(lambda x: x * x, filter(lambda x: x % 2 == 1, sequence))
Как видите, без нагромождения скобок программа выглядит гораздо изящнее
1. У нас нету координаты лошади. У нас есть объект «лошадь», которая как-то там грузится из некоего репозитория в persistence layer'е, а описывается этот момент XML-файлом, но алгоритм совершенно не зависит от…
2. А если у нас не PHP, то никто не мешает лошадке вообще жить в памяти, в зависимости от. И никуда она не денется, ибо AppDomain/тред с лошадкой будет жить, пока запущен веб-сервер
3. Вообще весь сайт может быть сделан как набор взаимодействующих друг с другом объектов (а вовсе не веб-страниц), которые как-то там маппятся на рельные view, описанные шаблонами.
4. Это всё отжирает память, согласен. Но уж лучше пусть хранится лишних два объекта, чем на каждый int уходит по 128 байт вместо 4 (да-да, это же PHP, глобальный и надёжный).
5. И лучше уж пусть отжирается память, чем всё время дёргается БД. Я, например, переписав чат на одном местном сайте на Java, получил огромный прирост производительности. Да, в PHP-ной версии использовался memcache. Но всё это кажется такими костылями, когда есть возможность держать очередь сообщений в памяти и отдавать ответы на запросы прямо из неё, вообще не делая новые коннекты к базе. Хотя какие нафиг коннекты? sessionFactory.openSession(). И ни о каких БД я не знаю!
6. В результате я получаю код, в который проще, в разы проще вносить изменения. Вот увидел я, что некие персистентные сущности тормозят работу сайта. Я просто настрою кэширование в Hibernate/Entity Framework. А в PHP со структурным подходом мне придётся мало того, что добавить кэширование данных при их запросе, так ещё и вставлять убийство «прокисшего» кэша во всех местах, где эти сущности как-то меняются.
7. Конечно, в PHP можно абстрагироваться, выделить слой работы с данными, слой представления. Но тогда мы либо используем средства ООП в PHP, либо, упрямствуя, получим точь-в-точь ООП как GTK+, без поддержки со стороны языка. И, кстати, оно будет тормозить систему. Ибо тот же ORM надо будет поднимать не при старте сервера, а при _каждом_ запросе. И, замечу, всяческие акселераторы не спасают.
Насчёт PostgreSQL. На SQL-рельсы он начал переезжать примерно во времена зарождения MySQL. А относительной стабильности добился и того позже.
Так вот, даже на таких «выразительных» языках объём кода в серьёзных проектах по-прежнему остаётся очень и очень немаленьким. Да, профессионал способен написать более выразительный код, но это отнюдь не означает, что объём кода стремится к нулю.
Ну а тезисом по поводу фреймворков ты меня даже позабавил. Ибо я сейчас к нему добавлю тезис «компилятор», и его смысл ничуть не изменится. А на самом деле «все эти фреймворки и библиотеки» нужны для того, чтобы не решать повторно уже решённые задачи. Прямо так и вижу, как труъ-профи пишет парсер XML вручную, при этом объём кода исчезающе мал…
По поводу IDE уже написал. IDE нужен ровно для того, чтобы увеличивать производительность программиста. И профессионал не будет писать в блокноте. Профессионал, во-первых, умеет грамотно пользоваться всеми фичами используемой IDE, а во-вторых, пишет код, который хорошо поддерживался бы со стороны IDE, т.е. свёл бы к минимуму использование всяческих кодогенераций, явных приведений типа в статически типизированных языках (хотя, тут даже не IDE, а компилятор), строкового описания различных сущностей в программе и т.п. и пользовался бы существующими механизмами языка.
1) увеличения выразительности языка
2) использования готовых фреймворков и библиотек
тем не менее, даже на «выразительных» языках вроде Python, Ruby или
И ещё про кодогенерацию. В теории оно конечно хорошо. А вот на практике… Из чего генерится код? Из XML? Из собственного языка? В первом случае ещё можно худо-бедно подсунуть IDE схему. Во втором — придётся писать «в блокноте». А поддержка со стороны IDE в разы повышает скорость кодирования, и на порядки — скорость внесения изменений.
Совсем непонятно, почему. Лично я думаю, что объяснение здесь высосано из пальца. Впрочем, объяснение в исходной статье тоже хромает, но она хотя бы есть.
А вот как показывает моя практика, не всегда это возможно делать. Да, я и сам стараюсь делать методы максимально компактными. Но иногда не выходит. Тут надо понимать, что нагородив методов, мы перейдём от спагетти в одном методе к спагетти из кучи непонятных private-методов. Да и не всегда методам удаётся дать внятное название, вот и получается набор методов вида DoMysteryousThing и DoSomeOtherMysteriousThing. Конечно, к ним можно подписать комментарии. Но! Почему бы тогда не написать всё в одном методе, разделив его этими самыми комментариями на логические блоки?
А вот тут автор вообще приводит абсурдный пример. Нехорошая это практика возвращать массив или bool. Тем более, это не прокатит в статически типизированных языках. А следующий пример — это возврат к тёмным временам C (поморщился). Вообще, если так уж нужно избежать выбрасывания исключения, то идеальное решение — вернуть структуру вроде Error, конкретизированную типом результата метода.
Мне не становится ясным ничуть. Вообще, своё мнение об уместности использования свойств я оставил в развёрнутом комментарии к исходной статье.
Вопрос к автору? А вы вообще осознаёте, что такое «функциональное программирование»? И при чём тогда OpenGL и «инициализация»?
Из приведённого ниже фрагмента кода вообще не ясно, почему не стоит делать, как он говорит. И, кстати, я бы этот код написал как-то так:
Да, как видите, не очень удобно. Зато не надо запоминать в какой последовательности надо передавать параметры. Хотя да, IDE зачастую в помощь. А вот читается код всё равно гораздо лучше. Разумеется, всё было бы ещё лучше, если бы были именованные параметры. Да, надо заметить, что обычно рисование происходит через некий «контекст», который хранится либо внутри самого объекта, через который производится рисование, либо передаётся параметром. Обычно этот контекст и содержит информацию о кистях, шрифтах, преобразованиях. Собственно, это то, про что автор хотел сказать в случае с OpenGL. Вот только к функциональному программированию это никак не относится. И с ООП, кстати, вполне себе уживается.
Это в некотором смысле аналог пункта 4 статьи.
Аналогично, в JS можно добавить методы map и filter в Array.prototype и так же пользоваться себе наздоровье точечной нотацией без увеличения уровня вложенности и дублирования названия helper-класса.
Haskell тут вообще не совсем уместен, так как он не ООП. Но вот и в нём есть средство (в виде операции $), которая позволяет избегать лишних скобок и записывать производимые операции последовательно, а не внутри друг друга.
что-то делает (ну, разве что кроме, например, ленивой загрузки, кэширования и т.п.), то bar — никакое не свойство.
И, кстати, раз уж статья рассказывает о несовершенстве языков, приводящем к «некрасивому коду», то могу напомнить ещё один недостаток. Касается он отсутствия возможности писать расширения к существующим классам. Выливается это в то, что приходится писать всевозможные helper-классы и напрямую писать что-то вроде:
вместо
В качестве «реальной» задачи для иллюстрации данного замечания могу предложить следующий пример. Дана последовательность целых чисел. Требуется найти квадраты нечётных чисел. Сравните пример на Haskell:
и на C# 3.0
с примером на Python
Как видите, без нагромождения скобок программа выглядит гораздо изящнее