Работаю я сейчас над одним проектом, над которым до меня уже трудилось 2 или 3 программиста. Вот уж где непочатый край всевозможных проявлений криворукости и кривоголовости Как говорится, для полного счастья тут еще программиста-индуса не хватало. Понятное дело, что каждый программист, увидев труд своих предшественников, обязательно должен начать рвать на себе волосы с криками «тут все надо переписать нахрен!». Я не стал действовать по шаблонному сценарию и перерабатываю код вдумчиво и по делу. Но сегодня речь не об этом.
Наверняка самые продвинутые мои читатели слышали про такие программерские заморочки, как MVC и CRUD. Как ни почитаешь литературу по теме, так все красиво, замечательно и просто чудесно. И логика у приложения в правильных местах сосредоточена, и код читать и понимать легче, и UX-часть не перегружена всякой билибердой. Но даже всякую красивую и правильную идею наши программисты могут извратить до безобразия.
В нашем приложении все как по учебнику: есть БД, есть хранимые процедуры, есть уровень модели (целая коллекция классов) и есть уровень представления. Контроллера, как это частенько бывает, в чистом виде нет, но на самом деле его функции успешно выполняет IIS. Каждый модельный объект следует паттерну CRUD, т.е. содержит методы Insert, Select, Update и Delete. Методы тупые — они просто вызывают соответствующие хранимые процедуры, которые в свою очередь также весьма тупые и по большей части состоят из одной строчки. Т.е. явно имеем ситуацию, когда программист тупо следует best practices не включая мозги. Ну да и хрен бы с ним, overhead от этого всего небольшой, работает все нормально, если нужно мутить какую-то логику, например, каскадное удаление, то достаточно поколдовать в базе и не трогать код. Но на этом хорошая сторона вопроса заканчивается и начинается серия идиотизмов.
Идиотизм первый. Все методы CRUD сделаны статическими. Я соглашусь с тем, что это относительно справедливо для создания новых объектов, т.е. для метода Insert, но в ситуации с Update данный подход приводит к появлению бредового кода на уровне представления/контроллера: для того, чтобы изменить значение одного (!!!) поля нужно сначала «прочитать» объект целиком из БД, а потом сделать вызов статического метода Update с кучей параметров (так уж получилось, что здесь встречаются объекты с 40-50 свойствами). Думаю не нужно объяснять, что такой подход не только не добавляет читабельности коду, но и приводит к выполнению лишних операций с БД. Кстати, с созданием объектов тоже не все гладко. Например, вы создаете объект для того, чтобы дальше с ним работать. Но в нашем случае это не прокатывает, т.к. статический метод Insert просто вызывает хранимую процедуру и все. Экземпляр объекта при этом не создается, свойства его не заполняются, хотя все данные для этого методу переданы. Что делает наш горе-программист в коде? Правильно, после Insert вызывает Select, чтобы прочитать из базы то, что только что туда записал.
Идиотизм второй. Не надо быть семи пядей во лбу, чтобы понимать, что в современном приложении редко какой объект существует сам по себе, чаще всего объекты взаимосвязаны (ссылаются друг на друга). Что же тут замыслил наш программист? Все правильно: храним внутри ссылки на взаимосвязанные объекты и инициализируем их только тогда, когда к ним кто-то обратится. Казалось бы, все чотко. Но и здесь наш программист абстрагировался от суровой действительности и не подстелил соломки там, где это было бы полезно. Объясню на примере. Предположим, есть объект Товар. Вполне логично, что он ссылается на такие объекты как Поставщик и Производитель. Также очевидно, что если мы предлагаем какое-либо хитрое ценообразование, то будет еще и ссылка на объект Цена. Предположим, что мы выводим на экран (web-страницу) список товаров, для чего у объекта Товар есть (как водится) статический метод SelectGoodsInCategory. Метод возвращает список объектов, полученный в результате одной команды select на сервере. Не нужно быть гением, чтобы догадаться, что на экран мы будем выводить название товара, его характеристики, производителя и цену. Что мы в итоге получаем? Для вывода списка из 20 позиций мы вынуждены выполнить 41 select на сервере (1 со списком товаров, 20 для получения 20-ти объектов производителей и 20 для получения 20-ти объектов цен). Любой нормальный программист задумается, но только не наш
Идиотизм третий. Уровень модели, т.е. эти самые объекты, не содержит в себе бизнес-логики вообще. Т.е., конечно, чуть-чуть кое-где попадается немножко логики типа «запустить задачу», но в основном вся эта логика живет где на уровне контроллера, а где и на уровне представления данных! И получается, что толку от этих объектов никакого нет, ибо все равно всю логику нужно каждый раз писать руками, транзакции не поддерживаются и т.п. Пиндык красивой идее
А самое интересное, что весь этот огород прекрасно работал до тех пор, пока количество объектов измерялось тысячами. Когда их перевалило за 50 тысяч, начало подтормаживать, но эта проблема была решена классическим современным способом — апгрейдом железа. После перехода границы в 500 тысяч все начало тупо падать, то по причине нехватки памяти, то по причине таймаутов при выполнении «элементарных» запросов. А я, помнится, удивлялся, как связка из 3-х (!!!) серверов (2 SQL + 1 WEB) с общим объемом в 24 гига памяти может нещадно тормозить, обслуживая 2 сайта с дай бог 100 уникальными пользователями в сутки.
Что молодым программистам стоит уяснить, так это то, что не нужно каждый раз тупо и слепо следовать какому-то шаблону или правилу или принципу. Это не математика и не физика, здесь законы и правила гибкие и никто не мешает вам их обходить или адаптировать. Всегда нужно думать не только о том, насколько «правильный» код ты напишешь, но и о том, что твой код должен быть эффективен.