Технологии

Функции или классы — мысли в слух 

Раньше я думал, что программирую плохо, потому-что мне не хватает знаний о теории программирования. Теперь, когда в башке сидит куча теоретической информации — типа «принцип открытия закрытия«, «правило подстановки Барбары Лискоу«, «принцип инверсии зависимостей» и т.д. Я понимаю, что программирую плохо, потому-что слишком много думаю о том, что пишу. Но продолжая испытывать неудовлетворенность от написанного кода, я пытаюсь найти пути его улучшения. Сегодня задумался о том, почему на каждый «чих» нужно писать класс, чем функции хуже?

Почему класс, а не функция?

Итак, первый вопрос — на кой черт нам вообще нужны классы, если есть возможность использования функции? Простого и очевидного ответа на этот вопрос я не знаю. Поэтому, попробую поразмышлять на примере.

Допустим у меня есть задача — объединить содержимое всех текстовых файлов из одной директории. Решить данную задачу можно одной функцией, что-то типа:

function concat($dir) {
//... здесь  обходим директорию и возвращаем содержимое всех найденных файлов.
}

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

Например, ситуация: хочется объединить все javascript файлы в один, для того, чтобы клиент (браузер) мог получать все скопом, а не по отдельности. На Кохане (Kohana) это может быть следующий action:

class Controller_Loader extends Controller {

 

public function action_all_javascript()
{
echo concat('./js/');
}
}

И опять не вижу в этом, ничего плохого! Кроме одного «но» — как правило, нужно загружать файлы не в произвольном порядке, а во вполне определенном. Для этого проставляю в начале каждого файла номер, соответствующий порядку его загрузки, например: «1core.js«, «2app.js» и т.д. Для того, чтобы не менять контроллер правлю функцию таким образом, чтобы она перед объединением файлов отсортировала их в нужном порядке.

С этого момента можно твердо сказать — код начал загнивать. Т.е. пока не было требования сортировки файлов, вполне можно было обойтись функцией. Теперь же, когда функция решает аж три задачи — сканирование директории, сортировка файлов в нужном порядке и объединение полученных файлов. Код получается невероятно хрупким. В результате, каждое новое требование будет падать на плечи неподъемным грузом.

А что если сделать несколько функций?

Раз новые требования вызывают сложности, то почему не сделать несколько функций вместо одной? Ведь до сих пор не понятно нужен ли класс в данной ситуации!

Ввожу дополнительное требование — порядок объединения файлов касается не только файлов, но и директорий, т.е. необходимо объединить файлы в текущей директорий, затем найти поддиректорию у которой наименьший порядковый номер и объединить файлы в ней и так рекурсивно обойти все дерево. Логика сложна даже для описания, а для реализации подавно.

Одной функцией здесь уже не обойтись, поэтому делаю блюдо из трех функций:

class Controller_Loader extends Controller {

 

public function action_all_javascript()
{
$fileList = scan_tree('./js/');
$sortedFileList  = sort_files_tree($filesList);
echo concat($sortredFileList);
}
}

Преимущества:
Во-первых, в параметрах функций используются простые типы — строка и массив, а это значит что функции получились абсолютно не связанными;
Во-вторых, можно вводить новые сортировки и изменять порядок сканирования, используя указатели на функции.

Недостатки:
Во-первых, появился некий контекст — список файлов и содержимое фалов (явный намек на создание класса);
Во-вторых, логика функции sort_files_tree — невероятно сложная, а значит ее придется разбивать на отдельные подзадачи, что равносильно созданию дополнительных функций (уже появляется некоторая связанность);
В-третьих, добавление новых требований (например: обработка ошибок, объединение файлов определенного типа, разный порядок сортировки и т.д.) приведет к увеличению количества функций и, в конечном итоге, к дублированию кода;

Вывод

Очевидно, что дальше идея использовать функции вместо классов будет приносить все больше проблем, и меньше пользы. Хотя, повторюсь, в первоначальной задаче не было и намека на последующие дополнительные условия, поэтому использование функций казалось вполне оправданным.

Вывод из всего сказанного простой — функции не плохое решения для простых задач, для сложных предпочтительнее строить классы. Остается определить какие задачи простые, а какие сложные.

Мне кажется, что, для всех простых задач в PHP уже есть готовые функции, а если подходящей функции нет, то это говорит о том, что задача не такая уж и простая.

RU/KZ