Php code style conventions

The Single Responsibility Principle (SRP) — Принцип единственной ответственности

Существует лишь одна причина, приводящая к изменению объекта.


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

DIP — Dependency Inversion Principle

В любой объектно-ориентированной программе существуют зависимости (связи, отношения) между классами. Очевидно, что с ростом количества и силы зависимостей программа становится менее гибкой. Принцип инверсии зависимостей направлен на повышение гибкости программы за счет ослабления связности классов. Ряд источников утверждает, что суть DIP заключается в замене композиции агрегацией, мы рассмотрим это более детально.

Отношение композиции означает, что объекты одного из классов включают экземпляр другого класса. Такая зависимость является более слабой чем наследование, но все равно очень сильной. Более слабым, а значит гибким, является отношение агрегации — при этом объект-контейнер содержит ссылку на вложенный класс.

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

Допустим, мы разработали класс TextReceiver, который принимает по какому-либо каналу связи текст и расшифровывает его. При этом TextReceiver реализуется посредством класса TextDecription, ответственного за расшифровку текста, в связи с этим мы могли бы использовать композицию (верхняя часть рисунка):

Strategy pattern example

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

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

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

Подобная замена композиции агрегацией лежит в основе шаблона проектирования стратегия (Strategy) , однако принцип DIP является более общим. Согласно формулировке  принципа инверсии зависимостей от Роберта Мартина:

Таким образом, Мартин говорит о любых зависимостях, а не только об отношении «часть-целое», т.е. он не сводит принцип к замене композиции агрегацией. В качестве примера возьмем игру, где персонаж перемещается по лабиринту с артефактами:

Dependency Inversion Principle example

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

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

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

Open for Extension, Closed for Modification

Now, time for the ‘O’ – more formally known as the open-closed principle. Simply put, classes should be open for extension, but closed for modification. In doing so, we stop ourselves from modifying existing code and causing potential new bugs in an otherwise happy application.

Of course, the one exception to the rule is when fixing bugs in existing code.

Let’s explore the concept further with a quick code example. As part of a new project, imagine we’ve implemented a Guitar class.

It’s fully fledged and even has a volume knob:

We launch the application, and everyone loves it. However, after a few months, we decide the Guitar is a little bit boring and could do with an awesome flame pattern to make it look a bit more ‘rock and roll’.


At this point, it might be tempting to just open up the Guitar class and add a flame pattern – but who knows what errors that might throw up in our application.

Instead, let’s stick to the open-closed principle and simply extend our Guitar class:

By extending the Guitar class we can be sure that our existing application won’t be affected.

Final Thoughts

I guess my main point here is that SOLID principles are valuable tools in your toolbox, and that you should keep them in back of your mind when designing your next feature or application.

We’ve got another resource about object-oriented design vs. functional programming, which you can read here. 

See you soon, when I hope to talk about coupling and cohesion, but in the meantime, please leave comments, share with your friends and reach out to me @Jrebel_Java.

Want to See How Developers Work With Java in 2020?

Our latest Java developer productivity report shows the technologies and ideas that developers use in developing Java applications in 2020. You can grab a PDF version of the report at the link below.

Примечания

  1. Мартин, Роберт (Дядя Боб). . butunclebob.com. Дата обращения 3 мая 2016. (прим. отсылка на «first five principles», то есть в статье не используется акроним.)
  2. Мартин, Роберт (Дядя Боб). . objectmentor.com. Дата обращения 3 мая 2016.
  3. Sandi Metz (Duke University).  (недоступная ссылка). Дата обращения 3 мая 2016. Выступление на конференции «2009 Gotham Ruby Conference» в мае 2009.
  4. Роберт С. Мартин, Мика Мартин. Принципы, паттерны и методики гибкой разработки на языке C#. — СПб.: Символ-плюс, 2011. — С. 8. — 768 с. — ISBN 978-5-93286-197-4.
  5. .
  6. .
  7. .
  8. (1996).
  9. Мартин, Роберт (Дядя Боб). . objectmentor.com (2000). Дата обращения 14 января 2009.
  10. .

Why Use SOLID Object-Oriented Design Principles?

When developers design without using structured design principles like SOLID, they can create long-lasting problems for future developers working on the project, and limit potential success for the application they’re developing. These issues are commonly referred to as “code rot.”

Gross. So how can we identify future code rot? These signs probably indicate code rot to come:

  • Rigidity – small changes causes the entire system to rebuild.
  • Fragility – changes to one module causes other unrelated modules to misbehave. Imagine a car system in which changing the radio station affects windows.
  • Immobility – a module’s internal components cannot be extracted and reused in new environments. For example, if an application’s login module cannot be used in entirely different system then this module is immobile, caused by couplings and dependencies between different modules. The strategy is to decouple central abstractions from low-level details, like a particular database schema or UI implementation (web, desktop) or specific frameworks.
  • Viscosity – when building and testing are difficult to perform and take a long time to execute. When even a simple change is costly to make, and requires you to make changes in multiple places/levels.

Users expect to get some value out of the software they use. An application’s value is determined by whether it helps users do something better, increasing productivity or time or money and saving on “waste”. With high-value software, people often pay money in exchange for it.

But there is a secondary value that users get from great software, and I’d like to talk about that value because this is often the first thing people think of when talking of software values: behaviour.

Creating Secondary Value in Your Application

If software does what its users need without bug crashes and delays, then the secondary value of the software is high. Secondary value is achieved when the software meets the current needs of the user. But user needs change, and frequently; the behaviour that the software provides and what the user needs can easily get out of sync, leading to lower value. Your software must be able to keep up with the changing needs of your customer in order to keep the secondary value high. So here we come to the primary value of software, it has to be capable to tolerate and facilitate the ongoing change.

Imagine that your software currently meets the needs of users, but is really hard and costly to change. Here, users get unhappier due to the app’s inflexibility, and profitability is likely to decrease.

And now imagine other software that has low secondary value at first, but is easy and inexpensive to change. Profitability can go only up and users get happier and happier.

6.3 String Pool

Все строки, которые были заданы в коде в , во время работы программы хранятся в памяти в так называемом . — это специальный массив для хранения строк. Цель его создания — оптимизация хранения строк:

Во-первых, строки, заданные в коде, нужно все-таки где-то хранить. Код — это команды, а данные (тем более такие большие как строки) нужно хранить в памяти отдельно от кода. В коде фигурируют только ссылки на объекты-строки.

Во-вторых, все одинаковые литералы можно хранить в памяти только один раз. Так оно и работает. Когда код вашего класса загружается Java-машиной, все строковые литералы добавляются в , если их там еще нет. Если уже есть, просто используется ссылка на строку из .

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


Как это примерно работает:

Код Работа с StringPoll

Именно поэтому переменные и будут хранить одинаковые ссылки.

Метод

Ну и самое интересное: вы можете программно добавить любую строку в . Для этого нужно просто вызвать метод у -переменной.

Метод добавит строку в , если ее еще там нет, и вернет ссылку на строку из .

Код Примечание

Вряд ли вы будете часто пользоваться этим методом, однако о нем любят спрашивать на собеседованиях, поэтому лучше о нем знать, чем не знать.

SRP – принцип единой ответственности

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

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

Давайте рассмотрим пример:

В данном случае класс Employee не соответствует принципу SRP, т.к. несет две ответственности – добавление нового сотрудника в базу данных и создание отчета. Класс Employee не должен нести ответственность за отчетность, т.к. например, если через какое-то время вам скажут, что нужно предоставить отчет в формате Excel или изменить алгоритм создания отчета, вам потребуется отредактировать класс Employee.

Согласно SRP, один класс должен взять на себя ответственность, поэтому мы должны написать отдельный класс для генерации отчетов:

Interface Segregation

The ‘I ‘ in SOLID stands for interface segregation, and it simply means that larger interfaces should be split into smaller ones. By doing so, we can ensure that implementing classes only need to be concerned about the methods that are of interest to them.

For this example, we’re going to try our hands as zookeepers. And more specifically, we’ll be working in the bear enclosure.

Let’s start with an interface that outlines our roles as a bear keeper:

As avid zookeepers, we’re more than happy to wash and feed our beloved bears. However, we’re all too aware of the dangers of petting them. Unfortunately, our interface is rather large, and we have no choice than to implement the code to pet the bear.

Let’s fix this by splitting our large interface into 3 separate ones:

Now, thanks to interface segregation, we’re free to implement only the methods that matter to us:

And finally, we can leave the dangerous stuff to the crazy people:

Going further, we could even split our  class from our example earlier to use interface segregation in the same way. By implementing a Printer interface with a single print method, we could instantiate separate ConsoleBookPrinter and OtherMediaBookPrinter classes.

7.4 Преобразование потока в мэп


А вот преобразовать поток в мэп немного сложнее. Ведь каждый объект Map состоит из двух элементов — ключа и значения. Нам нужно придумать, как у элемента потока мы будем определять ключ, а как — значение.

Пример.

Давайте разберем, что тут происходит.

В первой строчке мы преобразовываем каждую строку в массив строк. Используя метод split, мы делим каждую строку на две части по символу «равно».

Во второй строке — метод — мы пропускаем через фильтр только те элементы-массивы, которые содержат ровно два элемента. Элемент был разбит на массив из трех элементов, и фильтр не пройдет.

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

У нас в качестве ключа будет первый элемент массива («a», «b», «c»), а в качестве значений — второй элемент массива: «2», «3», «4».

40 советов, которые навсегда изменят ваши навыки кодирования

Разбивайте большие куски кода на маленькие логически завершенные части. Если к моменту ухода с работы проблема не решена, выключите компьютер и оставьте ее на следующий день. Не думайте об этом. Если вы слишком долго не можете найти проблему или ошибку, вернитесь к ней позже. Часто лучшие решения проблем возникают у меня на пути из моего офиса в туалет. Сначала решите проблему, а затем напишите код. Не начинайте писать код, не зная, что делать. Не запоминайте код, а поймите логику. Если вы копипастите часть кода из Stack Overflow, убедитесь, что вы его понимаете. Научитесь правильно копировать. Если хотите чему-то научиться, тренируйтесь. Делайте примеры и заставляйте их работать, потому что читать о чем-то недостаточно. Изучайте код других людей, и пусть другие время от времени изучают ваш код. Парное программирование и ревью кода – хорошая идея. Не изобретайте колесо. Ваш код – лучшая документация. Знайте, как гуглить. Для этого нужно иметь опыт и много читать, чтобы знать, что искать. В будущем ваш код могут поддерживать другие люди, поэтому пишите читабельный код, не пытайтесь казаться самым умным. Лучший способ устранить ошибку с помощью Google – скопировать и вставить ее. Никогда не сдавайся, в конце концов, так или иначе, ты решишь эту проблему. Плохие дни рано или поздно пройдут. Отдых, Отдых и Отдых. Лучший способ решить проблему – это иметь спокойный ум. Научитесь использовать шаблон проектирования программного обеспечения. Шаблоны проектирования – это решение общих проблем в разработке программного обеспечения. Каждый шаблон похож на план, который вы можете настроить для решения типичной проблемы проектирования в вашем коде. (Не изобретайте колесо) Используйте инструменты интеграции и автоматизируйте как можно больше. Внедрение зависимостей является обязательным требованием. Следуйте принципам SOLID. Рефакторинг – тест – рефакторинг. Рефакторинг – это метод реструктуризации существующего кода, изменения и улучшения его внутренней структуры без изменения внешнего поведения. Обратитесь за помощью, если вам это необходимо. Не теряйте время. Практика сделает вас идеальным. Хотя иногда комментарии могут помочь вам, не обращайте на них слишком много внимания

Они, вероятно, уже устарели. Изучите свою среду разработки и обратите внимание на такую мощную IDE, ​​как IntelliJ IDEA. Повторно используйте компоненты. Разрабатывая веб-приложение, не забывайте о том, что мобильные устройства, на которых оно будет работать, имеют ограничения в мощности и пропускной способности. Не оптимизируйте и не проводите рефакторинг преждевременно. Для начала лучше иметь минимально жизнеспособный продукт. Никогда не выбирайте неэффективный способ сэкономить несколько минут

Когда пишете код, делайте все тщательно! Следуйте документированным стандартам. Пользователи – это не айтишники. Подумайте об этом, когда будете разрабатывать свой пользовательский интерфейс. Всегда используйте систему контроля версий, такую ​​как GitHub или Bitbucket, выполняйте небольшие и частые коммиты Git. Лучше использовать логи, чем делать отладку кода. Сохраните логи всех критических частей. Будьте последовательны при кодинге. Если вы используете стиль, всегда используйте один и тот же. Если вы работаете в большой команде, ориентируйтесь на единый стиль со своими коллегами. Не прекращайте изучать что-то новое, но прежде чем браться за новый язык или платформу, сосредоточьтесь на основах разработки программного обеспечения. И наконец, имейте терпение и любовь к тому, что вы делаете.

Что такое SOLID?

На самом деле SOLID является красивой аббревиатурой из букв пяти основных принципов, входящих в это понятие. Данное определение было предложено Робертом Мартином. В переводе с английского слово solid означает твердый, прочный, надежный, цельный. Такими же свойствами обладают и программы написанные с использованием этих принципов. Но это не волшебная палочка, которая может гарантировать вам написание выдающегося продукта. Однако, следование принципам SOLID значительно повышает вероятность этого. Ведь основной целью этих принципов является создание расширяемой и легко поддерживаемой системы.

7.1 Сбор элементов

И наконец-то мы дошли до самого интересного метода в классе — метода . Он используется для того, чтобы перейти от потоков к привычным коллекциям — , , и другим.

В метод нужно передать специальный объект — . Этот объект вычитывает все данные из потока, преобразует их к определенной коллекции и возвращает ее. А следом за ним эту же коллекцию возвращает и сам метод collect.

Все это сделано довольно хитро: объект имеет тип – у него аж три типа-параметра. Последний тип — это обычно и есть тип вроде . Поэтому компилятор может по этому типу подставить правильный тип результата самого метода .

Надеюсь, вы не сильно запутались. В любом случае, самому создавать объекты типа Collector вам не нужно. Достаточно просто воспользоваться уже готовыми объектами, которые возвращают статические методы класса .

Класс Collectors

У класса есть несколько статических методов, которые возвращают готовые объекты-коллекторы на все случаи жизни. Их несколько десятков, но мы рассмотрим самые основные:

Объект, который преобразует поток в список —
Объект, который преобразует поток в список —
Объект, который преобразует поток в список —
Склеивает элементы потока в одну строку
Преобразует элементы потока в
Группирует элементы, возвращает

The Reason for SOLID Principles

The SOLID principles were first conceptualized by Robert C. Martin in his 2000 paper, Design Principles and Design Patterns. These concepts were later built upon by Michael Feathers, who introduced us to the SOLID acronym. And in the last 20 years, these 5 principles have revolutionized the world of object-oriented programming, changing the way that we write software.

So, what is SOLID and how does it help us write better code? Simply put, Martin’s and Feathers’ design principles encourage us to create more maintainable, understandable, and flexible software. Consequently, as our applications grow in size, we can reduce their complexity and save ourselves a lot of headaches further down the road!

The following 5 concepts make up our SOLID principles:

  1. Single Responsibility
  2. Open/Closed
  3. Liskov Substitution
  4. Interface Segregation
  5. Dependency Inversion

While some of these words may sound daunting, they can be easily understood with some simple code examples. In the following sections, we’ll take a deep dive into what each of these principles means, along with a quick Java example to illustrate each one.


С этим читают