Границы применения ООП
Здесь я на одном конкретном примере покажу границы применения объектно-ориентированного программирования. Данную статью, я пишу от первого лица, так как это частный опыт конкретного человека. При этом вам будет важно знать, что я строгий сторонник ООП. Все что я программирую, я программирую строго в этой концепции и не приемлю никакие другие, и любое совмещение. До некоторого времени, меня это совершенно не подводило, и по роду своей деятельности, я мог создать любую объектную декомпозицию, и чем более я следовал объектным принципам (а их далеко не все преподают, пишут в книгах, и тем более редко применяют) — тем более строгая, ясная и действенная архитектура получалась. Поэтому с одной стороны, думаю читатель понимает, что критиковать ООП я хотел бы меньше всего, как это модно в определенной среде делать (типа «да, лишь один из подходов, но …»). Поэтому написанное не нужно понимать как критику, вместо этого, чтобы хорошо владеть инструментом, идеологией, всегда полезно знать границы применимости. Это позволяет еще более эффективно воспользоваться ООП, зная когда ты приближаешься к её границам.
Я собираюсь вам продемонстрировать как рушится фундаментальный принцип ООП: объект должен содержать ему присущие свойства, методы и взаимосвязи с другими объектами. Рушится данный принцип в определенных вырожденных условиях, о которых и пойдет речь далее. Под «рушится» мы будем понимать то, что если поставить цель соблюсти этот принцип мы получим существенно неэффективную программу по скорости работы, и это принципиально нельзя изменить если не нарушить этот принцип.
Итак имеем:
- Число объектов порядка ≈1000
- Каждый из объектов содержит несколько свойств
- Объекты образуют дерево. При этом каждый объект имеет ссылки на последующие узлы дерева и обратную ссылку на своего родителя
- Существует массив, в котором линейно находятся все узлы дерева
- Существуют ряд других массивов, которые по определенной логике содержат определенные узлы этого дерева
- Таким образом существует граф с сильно связанными между собой узлами
Такая конструкция, в общем виде, с той или иной силой связанности присутствует в любой серьезной программе. Можно даже сказать, что это математический или абстрактный вид любой программы.
Теперь специальные условия:
- Такой граф образуется в программе по определенным предметным правилам, при этом для его построения нужно провести существенные вычисления
- Существует управляющий класс, который по определенной логике рассчитывает значения свойств каждого из объекта
- Расчет зависит от связей с другим объектами
- Важно, что основной цикл динамики программы заключается в нахождении такого набора свойств, который отвечает определенным условиям
- Поэтому возникает необходимость в создании копий (клонировании) всей структуры из ≈1000 объектов, произведении вычислений над этой копией, и если она не удовлетворяет условиям, то удаляется и возвращаемся к начальной структуре. И далее по циклу
Проблема возникает в таком малозаметном месте как создание копии. Тут нужно понимать, что в C# по умолчанию клонируются только свойства объекта, а связи с остальными объектами нет. Точнее копируются ссылки на связанные объекты. Так как нам нужно провести полное клонирование, то скопированные ссылки будут указывать на старые объекты, которые еще не проклонированы. А так как у нас граф, то нельзя постепенно шаг за шагом провести клонирование, так как мы попадем в петлю. Если делать произвольные разрывы, то мы не проконтролируем как в каких объектах обновились связи с другими, так как еще не будет проклонированы все объекты, с которым связан данный.
Логика же организации этого графа достаточно сложна и уже выполнялась. Но у нас нет ни какого другого варианта, как снова её повторить, так как в принципе эта логика и указывает какой объект с каким связывать.
Сериализация данных объектов в файл и восстановление в копию объекта займет еще большее время. Также как и в других языках копирование по значению (если оно поддерживается).
Таким образом, чтобы начать расчеты данных, мы вынужденны проводить клонирование структуры, которая в принципе не меняется.
И тут приходится нарушать принцип ООП. Создавать единожды структуру взаимосвязанных объектов и структуры данных этих объектов. Тогда воссоздавать граф взаимосвязи объектов нам нет необходимости. И далее клонируются только расчетные данные, линейно (а не иерархически) связанные с каждым объектом.
Таким образом, выигрыш по времени составляет порядка ≈2-3 раз. А значит чистота соблюдения объектных принципов стоит замедления программы как минимум вдвое.