Семь хороших объектно-ориентированных привычек при программировании на PHP (исходники)Источник: ibm Натан А. Гуд, инженер по программному обеспечению, консультант
На начальном этапе развития языка программирования PHP присущие ему ограничения делали PHP-код исключительно процедурным. Процедурный код - это программный код, в котором в качестве стандартных блоков для построения приложения применяются процедуры. Процедуры предполагают определенный уровень повторного использования благодаря возможности их вызова другими процедурами. Тем не менее, даже без специальных объектно-ориентированных языковых конструкций программист может реализовать в PHP-коде некоторые принципы ООП. Подобное смешение парадигм (процедурный язык с ОО-элементами) несколько затрудняет работу программиста и может осложнить чтение программного кода. ОО-конструкции в PHP-коде - позволяющие создавать и применять классы, строить отношения между классами с использованием наследования, определять интерфейсы - существенно упрощают написание программного кода, соответствующего методикам ООП. В то время как чисто процедурные проекты без большой степени модульности ведут себя достаточно хорошо на этапе исполнения, преимущества ООП-подхода проявляются при последующем сопровождении. Поскольку у типичного приложения большая часть жизни приходится на период его эксплуатации, сопровождение программного кода приложения выливается в большие расходы за срок его службы. Кроме того, об этих аспектах можно легко забыть во время разработки. Если вам необходимо как можно быстрее создать и развернуть свое приложение, то последующее удобство сопровождения в долгосрочной перспективе может отойти на второй план по сравнению с более актуальной задачей - достижением немедленной работоспособности. Модульность - это одна из ключевых характеристик хорошего ОО-проекта, оказывающая существенное содействие на этапе его сопровождения. Модульность помогает инкапсулировать изменения, что облегчает последующее расширение и модификацию приложения. Несмотря на то, что общее количество "полезных привычек" при построении ОО-программ существенно превышает число 7, описываемые в данной статье привычки - это именно то, что необходимо вам для приведения своего программного кода в соответствие с базовыми критериями ООП. Эти семь привычек образуют устойчивый фундамент, на котором вы сможете строить свои следующие ОО-подходы и создавать программное обеспечение, которое будет достаточно простым в обслуживании и в расширении. Эти привычки нацелены на несколько ключевых аспектов модульности. Итак, семь хороших объектно-ориентированных привычек при программировании на PHP:
В данном случае быть скромным - значит избегать выставления своей информации в своих реализациях классов и функций. Сокрытие своей информации должно стать основополагающей привычкой. Вы будете испытывать трудности при выработке всех остальных привычек до тех пор, пока не привыкнете скрывать детали своей реализации. Для сокрытия информации есть еще один термин - инкапсуляция. Вследствие множества причин непосредственное выставление полей в виде public является плохой привычкой; и самая важная из этих причин следующая: вы лишаете себя возможности что-либо изменить в своей реализации в случае необходимости. Концепции ООП используются для того, чтобы изолировать изменения; инкапсуляция играет здесь центральную роль - она гарантирует, что любые вносимые вами изменения не будут вирулентными по своей природе. Вирулентные изменения начинаются с малого, например, с преобразования трехэлементного массива в массив, содержащий всего два элемента. Внезапно вы обнаруживаете, что для того, чтобы адаптироваться к этому, казалось бы, тривиальному изменению, вам приходится вносить в свой код все больше и больше других изменений. Один простой способ для перехода к сокрытию своей информации состоит в том, чтобы оставлять поля в статусе private и выставлять их с помощью public методов-аксессоров, которые ведут себя как окна в вашем доме. Вместо того чтобы открывать наружу всю стену, вы используете всего одно или два окна. Использование public-аксессоров вместо непосредственного выставления полей позволяет вам менять свою реализацию "за занавесом". Кроме того, это позволяет вам продлить использование своей базовой реализации посредством переопределения реализации аксессора, чтобы он смог делать нечто отличающееся от поведения своего родителя. Это также позволяет строить абстрактные реализации, которое отодвигают фактическую реализацию до момента создания классов, переопределяющих базовую реализацию. Плохая привычка: выставление полей как public В примере "плохого" кода в листинге 1 аксессоры не применяются, а поля объекта Листинг 1. Плохая привычка: выставление полей как public
Если в объекте что-либо меняется, то любой код, который использует его, также должен измениться. Например, если имя, фамилия и другие атрибуты определенного человека необходимо инкапсулировать в объект Хорошая привычка: использование public-аксессоров При использовании хороших ОО-привычек (см. листинг 2) тот же самый объект теперь имеет поля типа private (вместо полей public), и эти поля private с соблюдением мер предосторожности выставлены внешнему миру с помощью методов Листинг 2. Хорошая привычка: использование public-аксессоров
На первый взгляд может показаться, что это существенно увеличивает объем работы, особенно на фронтальной части. Следует, однако, отметить, что в большинстве случаев использование хороших ОО-привычек в конечном счете окупается, поскольку существенно упрощает будущие изменения. В версии кода, показанной в листинге 3, я изменил внутреннюю реализацию, использовав ассоциативный массив для отдельных частей имени. В идеале мне следовало бы уделить больше внимания обработке ошибок и соблюдать большую осторожность, в частности, проверять элементы на существование, однако у этого примера несколько иные цели: я хочу показать, что код, использующий мой класс, не нуждается в изменениях - он находится в блаженном неведении об изменениях в моем классе. Помните, что причина для принятия хороших ОО-привычек состоит в том, чтобы тщательно инкапсулировать изменения и таким образом улучшить расширяемость и сопровождаемость кода. Листинг 3. Другая разновидность этой хорошей привычки с иной внутренней реализацией
Когда вы создаете какой-либо класс, он должен обрабатывать свои собственные ошибки надлежащим образом. Если класс не знает, как обращаться с такими ошибками, он должен упаковывать их в формате, понятном для вызвавшего его класса. Кроме того избегайте возвращать объекты с состоянием null или invalid. Во многих случаях этого можно добиться посредством простой верификации аргументов и выдачи определенных исключений, объясняющих, почему предоставленные аргументы недопустимы. Когда вы усвоите эту привычку, она поможет сэкономить много времени и вам, и тем, кто будет сопровождать ваш код или использовать ваши объекты. Плохая привычка: отсутствие обработки ошибок Рассмотрим показанный в листинге 4 пример, в котором принимаются некоторые аргументы и возвращается объект Листинг 4. Плохая привычка: отсутствие выдачи или обработки ошибок
Метод Хорошая привычка: каждый модуль сам обрабатывает свои ошибки Вместо того чтобы заставлять своих "вызывающих" строить догадки, позаботьтесь заранее о проверке аргументов. Если не установленное значение переменной может привести к недостоверному результату, проверьте эту переменную и выдайте исключение Листинг 5. Хорошая привычка: выдача ошибок
Практический результат состоит в том, что ваш класс смогут использовать другие люди, при этом им не потребуются знания о том, что происходит внутри этого класса. Если эти люди станут использовать ваш класс неправильным или не предназначенным для него способом, им не придется гадать о том, почему он не работает. Как хороший сосед, вы отдаете себе отчет в том, что люди, которые будут повторно использовать ваш класс, - не телепаты, и поэтому избавляете их от необходимости догадок.
Когда я впервые изучал концепции ООП, у меня были определенные сомнения относительно того, являются ли интерфейсы действительно полезными. Мой коллега предложил следующую аналогию: не использовать интерфейсы - это то же самое, что смотреть в лицо Медузы Горгоны. Согласно древнегреческой мифологии, Медуза Горгона - это женское существо со змеями вместо волос. Любой человек, который смотрел на нее непосредственно, превращался в камень. Персей, который убил Медузу, смог противостоять ей, глядя на ее отражение в своем отполированном щите, и таким образом избежав превращения в камень. Интерфейсы - это ваше зеркало при обращении с Медузой Горгоной. Если вы применяете специализированную, "негибкую" реализацию, то в случае изменения этой реализации ваш код также должен будет измениться. Непосредственное использование реализаций ограничивает перечень доступных вам вариантов, поскольку, образно говоря, вы обратили свои классы в камень. Плохая привычка: отсутствие интерфейсов В примере в листинге 6 объект Листинг 6. Плохая привычка: отсутствие интерфейсов
Код для загрузки Хорошая привычка: использование интерфейсов В листинге 7 показан пример кода, который не нуждается в изменениях в случае появления и реализации новых способов загрузки пользователей. В примере показан интерфейс с именем Листинг 7. Хорошая привычка: использование интерфейсов
При использовании интерфейсов старайтесь избегать непосредственного обращения к классам реализации. Вместо этого для корректной реализации используйте нечто "внешнее" по отношению к своему объекту. Если ваш класс загружает реализацию на основе определенной логики, то ему по-прежнему придется потребовать определения для всех классов реализации. Таким образом, вы остались на прежнем месте. С помощью шаблона Factory вы можете создать экземпляр класса реализации, который и реализует ваш интерфейс. В соответствии с принятыми соглашениями, метод В листинге 7 метод
Применяйте самое слабое связывание Слабое связывание модулей является весьма хорошим приемом; слабое связывание - это одно из свойств, которые позволяют инкапсулировать изменения. Две рассмотренные выше привычки - "скромность" и "использование интерфейса для работы с Медузой Горгоной" - помогают создавать именно слабо связанные модули. Чтобы добиться слабого связывания своих классов, выработайте привычку к ослаблению их зависимостей. Плохая привычка: сильное связывание Как показано в листинге 8, ослабление зависимостей - это не обязательно ослабление зависимостей для клиента, использующего тот или иной объект. Скорее, в этом примере демонстрируется ослабление зависимостей для соответствующего класса и сведение их к минимуму в остальных местах. Листинг 8. Плохая привычка: сильное связывание
Код, который вызывает метод Класс Хорошая привычка: слабое связывание между объектами При построении хороших ОО-проектов необходимо придерживаться концепции "разделения ответственности" (Separation of Concerns, SoC). В соответствии с этой концепцией объекты разделяются по возлагаемой на них ответственности, что существенно ослабляет связывание между ними. В исходной ситуации на класс Как показано в листинге 9, код, который ранее форматировал адрес, теперь перемещен в интерфейсы, в классы реализации и в factory - в соответствии с привычкой к "использованию интерфейсов". Теперь класс Листинг 9. Хорошая привычка: слабое связывание между объектами
Оборотная сторона этого подхода состоит в том, что при любом использовании шаблонов возрастает количество артефактов (классов, файлов). Тем не менее эта тенденция компенсируется уменьшением объема сопровождения в каждом классе, причем степень этой компенсации значительно возрастает при достижении надлежащего уровня многократного использования.
ОО-проекты с т.н. "высоким сцеплением" (high cohesion) отличаются сфокусированностью и организованностью в виде модулей с внутренним родством. Знание "ответственности" (см. предыдущий раздел) позволяет организовать функции и классы таким образом, чтобы они обладали высоким сцеплением. Плохая привычка: низкое сцепление Низкое сцепление проекта означает, что его классы и методы сгруппированы ненадлежащим образом. Для описания классов и методов, которые объединены друг с другом, но при этом имеют низкое сцепление, часто используется термин спагетти-код. Пример спагетти-кода показан в листинге 10. Сравнительно универсальный (generic) класс Листинг 10. Плохая привычка: низкое сцепление
Хорошая привычка: высокое сцепление Высокое сцепление проекта означает, что родственные классы и методы объединены в группы. Если методы и классы обладают высоким сцеплением, вы сможете легко выделить их из состава группы без ущерба для всего проекта. Проекты с высоким сцеплением создают условия для ослабления связности. В листинге 11 показана более эффективная организация методов в классы. Класс Listing 11. Хорошая привычка: высокое сцепление
Участникам групп разработки программного обеспечения, в которых я являюсь техническим руководителем или архитектором, я часто повторяю, что основной враг ОО-языков - операция "копировать-вставить". Ничто другое не способно нанести большего вреда, чем копирование кода из предыдущего файла в следующий файл - без заранее подготовленного ОО-проекта. Каждый раз, когда вы испытываете искушение скопировать код из одного класса в другой, остановитесь и подумайте, каким образом вы могли бы использовать иерархии классов для реализации подобных или идентичных функциональных возможностей. Вы обнаружите, что в большинстве случаев при наличии хорошего проекта в копировании кода нет абсолютно никакой необходимости. Плохая привычка: отсутствие использования иерархий классов В листинге 12 показан простой пример частичных классов. В этих классах применяются дублирующиеся поля и методы - что не очень хорошо в долгосрочной перспективе, когда, возможно, придется модифицировать данное приложение. Если в классе Листинг 12. Плохая привычка: отсутствие использования иерархий классов
Выработать привычку к использованию наследования трудно, поскольку во многих случаях анализ, необходимый для построения надлежащих моделей наследования, может отнять много времени. И наоборот, использование Ctrl+C и Ctrl+V для построения новой реализации занимает всего несколько секунд. Следует, однако, отметить, что обычно потраченное время окупается достаточно быстро на этапе сопровождения, который фактически занимает большую часть жизненного цикла приложения. Хорошая привычка: использование наследования В листинге 13 показан новый класс Листинг 13. Хорошая привычка: использование наследования
Шаблоны проектирования - это проверенные временем типовые взаимодействия объектов и методов при решении определенных проблем. Когда вы мыслите шаблонами проектирования, вы заставляете себя осознавать, как классы взаимодействуют друг с другом. Шаблоны - это удобный способ построения классов и их взаимодействий без повторения всех тех ошибок, которые уже сделали в прошлом другие. Кроме того, они позволяют воспользоваться проверенными конструкциями. Плохая привычка: рассмотрение отдельно взятых объектов Не существует каких-либо действительно адекватных примеров кода, которые позволили бы продемонстрировать мышление в шаблонах (хотя есть масса хороших примеров, демонстрирующих реализацию шаблонов). В общем случае можно говорить, что вы рассматриваете каждый объект в отдельности, если соблюдаются следующие критерии:
Хорошая привычка: согласованное добавление объектов в составе шаблонов В общем случае вы мыслите в шаблонах, если соблюдаются следующие критерии:
Выработка хороших объектно-ориентированных привычек при программировании на PHP помогает создавать более устойчивые, легко сопровождаемые и проще расширяемые приложения. Итак, не забывайте о следующих привычках:
Когда вы сформируете у себя эти привычки и станете руководствоваться ими в своей повседневной деятельности, вы, вероятно, будете приятно удивлены изменениями в качестве ваших приложений. |