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

Объектно-ориентированное программирование

Первый принцип, который мы обсудим, — это «объектно-ориентированное программирование», отличительная черта парадигмы программирования, которая поддерживается многими языками, включая, но не все; Ада, ActionScript, C++, Common Lisp, C#, Dart, Eiffel, Fortran 2003, Haxe, Java, JavaScript, Kotlin, логотип, MATLAB, Objective-C, Object Pascal, Perl, PHP, Python, R, Raku, Ruby, Scala , SIMSCRIPT, Simula, Smalltalk, Swift, Vala и Visual Basic.NET.

Объектно-ориентированное программирование (ООП) — это парадигма программирования, которая организует код в повторно используемые и модульные структуры, называемые объектами. По своей сути ООП основано на концепции «объектов», которые являются экземплярами классов. Класс — это проект или шаблон, определяющий свойства (атрибуты) и поведение (методы) объектов.

В объектно-ориентированном программировании есть четыре основных принципа:

1. Инкапсуляция:

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

В приведенном выше примере кода наш объект Place имеет атрибуты и методы в одном классе и файле. Это позволяет разработчикам легко получать доступ к методам и атрибутам, относящимся к «местам», просто обращаясь к ним прямо из экземпляра класса Place.

В приведенном выше коде мы легко запрашиваем экземпляр Place, который нам нужен, просто выполняя поиск в нашей базе данных по атрибуту «id», который хранится в классе Place. Мы также можем манипулировать атрибутами в нашем экземпляре place, вызывая метод, встроенный в объект Place. Все это просто и не утомляет разработчиков, заставляя их понимать, как правильно манипулировать атрибутами Place.

2. Наследование:

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

3. Полиморфизм:

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

Пример ниже демонстрирует как применение наследования, так и полиморфизма.

Приведенный выше код является примером наследования, когда класс «ProfilePicture» наследуется от базового класса «View» Django, что означает, что класс «ProfilePicture» получает все те же методы и атрибуты, что и родительский класс «представление», с возможность переопределения указанных методов и их изменения.
Это приложение наследования позволяет нам также применять полиморфизм, имея возможность просто переопределить метод представления «get» с нашей реализацией, но при этом иметь другое поведение от остальной части. 'view', который позволяет нам отправлять HTTP-запрос GET напрямую, не указывая, что нам требуется заголовок метода 'GET' в нашем запросе, потому что мы все еще используем URLconf, указанный в классе 'view'.

4. Абстракция:

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

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

Твердый принцип

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

Принцип единой ответственности (SRP):

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

В приведенном выше примере наш класс Profile имеет два основных HTTP-метода: маршрут GET предоставляет информацию о профиле пользователя, а маршрут POST предоставляет способ изменить изображение профиля пользователя.
Хотя с функциональной точки зрения в этом нет ничего плохого. , два метода означают, что класс Profile имеет две обязанности и, таким образом, нарушает наш принцип, поэтому может быть логичнее выделить второй метод в отдельный класс, в качестве побочного эффекта это сделает наш код более читабельным и удобным для других разработчиков. .

Открытый/закрытый принцип (OCP):

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

Принцип замены Лисков (LSP):

LSP утверждает, что объекты суперкласса должны заменяться объектами его подклассов, не влияя на правильность программы. Другими словами, любой производный класс должен иметь возможность использоваться вместо своего базового класса. Соблюдение LSP гарантирует, что поведение программы останется согласованным при замене объектов, и поможет сохранить целостность иерархии наследования.

Примером как OCP, так и LSP может быть принцип абстракции из ранее обсуждавшегося объектно-ориентированного программирования, поскольку принципы абстракции, Open/Closed и LSP подобны горошине в стручке.

Принцип разделения интерфейсов (ISP):

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

Принцип инверсии зависимостей (DIP):

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

Заключение

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

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