- PVSM.RU - https://www.pvsm.ru -
(Перевод)
Последние несколько лет я провел в изучении и экспериментах со многими языками программирования. В частности, я начал использовать Scala как основной язык, стараюсь использовать функциональный стиль везде где это возможно. Меня также весьма заинтересовал Haskell (чистый функциональный язык) и Clojure (современный диалект Лиспа).
Таким образом, я постепенно отказываюсь от объектно-ориентированной парадигмы, несмотря на то, что использовал в основном её последние 17 лет моей профессиональной деятельности. У меня появляется чувство, что объекты это то, что мешает нам писать лапидарный, структурированный и повторно используемый код.
Когда я задумываюсь на эту тему, я понимаю что в моём
В начале девяностых объекты были новыми и интригующими. Приверженцы этой новой парадигмы обещали возможность создания повторно используемых классов и паттернов, и это звучало заманчиво. Возможность комбинировать эти классы в многократно используемые и настраиваемые бизнес компоненты казалась Святым Граалем индустрии ПО. Разработчики новых языков, таких как C++ и позднее Java, поддержали пропаганду ООП как способа делать классное ПО.
Вскоре стало понятно, что возможность создавать повторно используемые бизнес компоненты это заблуждение. Каждый бизнес отличается от всех остальных, даже в одной отрасли. Каждый похожий проект работает по слишком специфичной бизнес логике.
Единственный способ сделать повторно используемые бизнес компоненты на этом уровне — сделать их сверх-настраиваемыми путём добавления таких штук как движки правил и встраиваемые языки. Такую модель я бы не стал называть моделью компонента. Скорее моделью очередного экземпляра bloatware. Обещание о повторном использовании ушло, люди либо покупали огромные bloatware системы(неудачники), или разрабатывали свои специальные бизнес объекты в своих собственных проектах.
Следующая вещь которую мы поняли — надежды на паттерны проектирования не оправдываются. Паттерны не приводят к хорошей структурированности. Напротив, они приводят к чрезмерному усложнению программ, трудностям в понимании кода и дороговизне сопровождения. Некоторые паттерны стали даже анти-паттернами (например, паттерн singleton приводит к невозможности создания модульного тестирования).
Лишь недавно программисты научились использовать паттерны осмысленно. Намного проще писать код так, как вы понимаете модель сами. А не пытаться абстрагировать его к какому-то обобщённому паттерну из книги Александреску.
Ещё одно обещание парадигмы объектов — развитые фреймворки тесно связанных классов, используемые вместе друг с другом. Парадигма обещала значительно упростить построение приложений из повторно используемых компонент, скрывая абсолютно все технические сложности и тонкости реализации. Имею в виду такие штуки как EJB. Однако опыт показал, что эти фреймворки не работают. Они просто слишком громоздкие и мешают людям делать то, что они хотят сделать.
Такие тяжелые фреймворки быстро умирают и замещаются более легковесными библиотеками и наборами легковесных библиотек. Стало понятно, что легче строить программы из наборов не так тесно связанных классов, которые вы можете скомпоновать вместе в зависимости от потребностей. Вам действительно не нужна тесная интеграция разных классов.
Возможность наследовать интерфейс и реализацию является одним из основных принципов ООП. Можно выделить общий код и поведение в базовый класс, при этом будущие абстракции могут использовать этот код и добавлять логику на его основе.
К сожалению, это не работает. Каждый подкласс немного отличается от своих родителей. Что ведёт к множеству изменений в дочернем классе. Или к попыткам сделать базовый класс более общим. В конечном итоге мы получаем хрупкий код. В котором маленькое изменение в базовом классе ведёт к поломке большинства, если не всех, порождённых классов.
Другой базовый принцип ооп — возможность инкапсулировать состояние и предоставлять поведенческие методы для работы с состоянием. К сожалению, получилось так, что в большинстве случаев нам нужны данные объекта целиком, а не методы работы с ними.
Например, нелепо просить объект отрендерить себя в HTML. Знание о рендеринге HTML в этом случае размазывается по проекту и небольшое изменение в коде рендеринга влечёт за собой изменение множества классов. Правильнее будет передать объект в отдельный комонент рендеринга HTML. И этот компонент прежде всего извлечёт все интересующие его значения из объекта.
Появились даже антипаттерны, которые позволяют создавать объекты с инкапсулируемым состоянием без поведенческих методов (Java Beans, Data Transfer Objects и т.п.). Если мы делаем такие объекты, почему бы не использовать просто структуры вместо объектов?
Ещё одно кажущееся преимущество инкапсуляции — возможность изменять состояние объекта без влияния на компоненты, которые используют этот объект. Однако, любой разработчик большого объектно-ориентированного проекта может рассказать истории о переборе огромного объёма кода, чтобы найти место, в котором состояние объекта меняется на ошибочное, что приводит к сбою в программе в абсолютно другом месте (обычно в момент, когда вы выводите или сохраняете состояние объекта).
Чем больше мы проявляем интерес к неизменяемому состоянию и stateless сервисам, тем больше убеждаемся, что там этих проблем нет. Другим преимуществом неизменяемого состояния является простота создания параллельных систем для повышения эффективности использования современных многоядерных процессоров. Это намного легче и безошибочнее, чем пытаться работать с потоками, блокировками и потокобезопасными структурами данных.
Итак, можно ли отбросить свой 17 летний опыт и задуматься о жизни без объектов? Я пока не уверен, что дошёл до полного просветления на этот счёт. Но использование мультипарадигменного языка Scala позволяет мне обойти многие ограничения ООП.
Например, наличие в Scala такой фичи как mixin traits позволяет полностью отказаться от наследования реализации. Использование немутабельных значений и коллекций делает код более простым в понимании и отладке.
Функции как обобщённые абстракции и классы типов для расширения поведения позволяют писать хорошо структурированный и повторно используемый код. Принцип single responsibilities в этом случае выполняется идеально.
Фактически, я понял что стараюсь как можно больше использовать простые объекты в форме case classes для представления структур данных. Методов у таких классов мало, они предназначены только для облегчения работы с данными. Для группировки функций я использую mixin traits. Компоненты, которые я проектирую, просто собирают различные связанные по смыслу функции, которые преобразуют данные из одной формы в другую.
Возможно, я намного дальше ушёл от чистого ООП чем мне кажется. С уверенностью могу сказать, что теперь я пишу более компактный, более понятный и лучше структурированный код чем прежде.
(Примечание переводчика: в этой статье хотелось бы акцентировать внимание на отношение опытного ООП программиста к ООП. Я не знаком с языком Scala, а соответственно и со специфичными для Scala терминами.
Всё чаще я вижу ситуации, когда опытные программисты рассказывают о том, что разочаровались в ООП.
По поводу возможностей Scala. Видимо, они действительно покрывают весь спектр случаев, с которыми приходится иметь дело в ООП. Хотелось бы добавить, что возможности Erlang или Haskell делают это ничуть не хуже.
)
Автор: tranquil
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/8311
Ссылки в тексте:
[1] мышлении: http://www.braintools.ru
Нажмите здесь для печати.