какой принцип ооп нарушает следующий фрагмент кода

10 принципов ООП, о которых стоит знать каждому программисту

какой принцип ооп нарушает следующий фрагмент кода

Многим опытным разработчикам, вероятно, знакома методология объектно-ориентированного программирования (ООП). Кроме известных её принципов (абстракция, инкапсуляция, полиморфизм, наследование и т. д.) существуют и другие — менее известные, но не менее важные и полезные для реализации. Некоторые из них собраны в специальный блок и известны по акрониму SOLID. Эта статья расскажет об этих и других существующих принципах объектно-ориентированной разработки и о том, какие преимущества они предлагают.

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

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

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

Принцип открытости/закрытости (OCP)

Соответствует букве O акронима SOLID. Принцип можно выразить так: «Классы, методы или функции должны быть открыты для расширения (добавления новой функциональности) и закрыты для модификации». Такой подход запрещает кому-либо изменять уже опробованный и протестированный код, а значит, он не ломается. В этом и состоит основное преимущество такого подхода.

Ниже приведён пример кода на Java, который нарушает этот принцип:

Принцип подстановки Барбары Лисков (LSP)

Соответствует букве L акронима SOLID. Согласно этому принципу подтипы должны быть заменяемыми для супертипа. Другими словами, методы или функции, работающие с суперклассом, должны иметь возможность без проблем работать также и с его подклассами.

LSP тесно связан с принципом единственной ответственности и принципом разделения интерфейса.

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

Ниже приведён пример такого кода на Java:

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

Соответствует букве I акронима SOLID. Этот принцип подразумевает, что интерфейс, который не используется, не должен быть реализован.

В основном это происходит, когда один интерфейс содержит несколько функциональностей, и клиенту нужна только одна из них, а другие — нет.

Написание интерфейса — сложная задача. Когда он готов, вы не сможете изменить его, не нарушив всю реализацию.

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

какой принцип ооп нарушает следующий фрагмент кода

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

Соответствует букве D акронима SOLID. Прелесть этого принципа проектирования в том, что любой класс легко тестируется с помощью фиктивного объекта и проще в обслуживании, потому что код создания объекта централизован, а клиентский код не перегружен им.

Ниже приведён пример кода Java, который нарушает принцип инверсии зависимости:

Теперь перейдём к принципам, которые не входят в пятёрку SOLID, но не менее важны.

DRY (Don’t Repeat Yourself)

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

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

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

Инкапсуляция изменяющегося кода

Сервисы стремительно развиваются. Продакшн подразумевает постоянные изменения кода и его поддержку. Отсюда следует второй принцип ООП — инкапсуляция кода, который с большой вероятностью будет изменён в будущем.

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

Воспользуйтесь алгоритмом, по которому переменные и методы по умолчанию имеют спецификатор private. Затем шаг за шагом увеличиваете доступ при необходимости (с private на protected, с protected на public).

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

Композиция вместо наследования

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

Композиция позволяет изменять поведение класса прямо во время выполнения через установку его свойств. Реализуя интерфейсы, вы, таким образом, используете полиморфизм, который обеспечивает более гибкую реализацию.

«Effective Java» Джошуа Блоха также советует отдавать предпочтение композиции вместо наследования. Если вы всё ещё не уверены, вы также можете посмотреть здесь, чтобы узнать, почему композиция лучше, чем наследование для повторного использования кода и его функциональности.

Программирование для интерфейса

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

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

Это также рекомендовано во многих книгах по Java, в том числе в Effective Java и Head First design pattern.

Ниже приведён пример для интерфейса в Java:

Принцип делегирования

Не делайте всё самостоятельно, делегируйте это в соответствующий класс. Классическим примером этого принципа являются методы equals() и hashCode() в Java. Если нужно сравнить два объекта, это действие поручается соответствующему классу вместо клиентского.

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

Заключение

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

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Cup, то бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

Источник

Принципы объектно-ориентированного программирования

Привет, Хабр! Меня зовут Владислав Родин. В настоящее время я являюсь руководителем курса «Архитектор высоких нагрузок» в OTUS, а также преподаю на курсах, посвященных архитектуре ПО.

Специально к старту занятий в новом потоке курса «Архитектура и шаблоны проектирования» я подготовил еще один авторский материал.

Введение

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

Вообще устроено все следующим образом: есть само объектно-ориентированное программирование. У него есть принципы. Из принципов объектно-ориентированного программирования следуют разобранные нам шаблоны GRASP (как вариант — SOLID принципы), из которых, в свою очередь, следуют шаблоны GoF. Из них же следует ряд интересных вещей, например, enterprise паттерны.

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

Определение гласит, что «Объектно-ориентированное программирование – это парадигма программирования, в которой основной концепцией является понятие объекта, который отождествляется с предметной областью.»

Таким образом, система представляется в виде набора объектов предметной области, которые взаимодействуют между собой некоторым образом. Каждый объект обладает тремя cоставляющими: идентичность (identity), состояние (state) и поведение (behaviour).

Состояние объекта — это набор всех его полей и их значений.

Поведение объекта — это набор всех методов класса объекта.

Идентичность объекта — это то, что отличает один объект класса от другого объекта класса. С точки зрения Java, именно по идентичности определяется метод equals.

Принципы объектно-ориентированного программирования

Объектно-ориентированное программирование обладает рядом принципов. Представление об их количестве расходится. Кто-то утверждает, что их три (старая школа программистов), кто-то, что их четыре (новая школа программистов):

Инкапсуляция

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

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

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

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

Наследование

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

Наследование является самым переоцененным принципом. Когда-то считалось, что «У идеального программиста дерево наследования уходит в бесконечность и заканчивается абсолютно пустым объектом», потому как когда-то люди не очень хорошо понимали то, что наследование — это способ выразить такое свойство реального мира как иерархичность, а не способ переиспользовать код, отнаследовав машину от холодильника, потому что у обоих предметов есть ручка. Наследования желательно по возможности избегать, потому что наследование является очень сильной связью. Для уменьшения количества уровней наследования рекомендуется строить дерево «снизу-вверх».

Полиморфизм

Полиморфизм — это возможность использовать классы – потомки в контексте, который был предназначен для класса – предка.

За самым садистским определением кроется возможность языка программирования для декомпозиции задачи и рефакторинга if’ов и switch’ей.

Источник

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

какой принцип ооп нарушает следующий фрагмент кода

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

Напоминаем: для всех читателей «»Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Skillbox рекомендует: Образовательный онлайн-курс «Java-разработчик».

DRY (Don’t Repeat Yourself)

Довольно простой принцип, суть которого ясна из названия: «Не повторяйся». Для программиста это означает необходимость ухода от дублирующего кода, а также возможность использовать в работе абстракцию.

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

Это нужно для того, чтобы упростить код и сделать его поддержку проще, что является основной задачей ООП. Злоупотреблять объединением тоже не стоит, поскольку один и тот же код не пройдет проверку как с OrderId, так и с SSN.

Инкапсуляция изменений

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

Принцип открытости/закрытости

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

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

Вот пример кода, который нарушает этот принцип.

какой принцип ооп нарушает следующий фрагмент кода

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

Кстати, открытость-закрытость — один из принципов SOLID.

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

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

какой принцип ооп нарушает следующий фрагмент кода

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

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

какой принцип ооп нарушает следующий фрагмент кода

Выше приведен пример кода, где AppManager зависит от EventLogWriter, который, в свою очередь, тесно связан с AppManager. Если нужен иной способ показать уведомление, будь это пуш, SMS или email, нужно изменить класс AppManager.

Проблема может быть решена при помощи DIP. Так, вместо AppManager мы запрашиваем EventLogWriter, который будет введен при помощи фреймворка.

DIP дает возможность без проблем заменять отдельные модули другими, изменяя модуль зависимости. Это дает возможность изменять один модуль, не влияя на остальные.

Композиция вместо наследования

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

Композиция дает возможность изменять поведение класса во время выполнения путем установки его свойств. При реализации интерфейсов используется полиморфизм, который дает более гибкую реализацию.

Даже “Effective Java” Джошуа Блох (Joshua Bloch) советует отдавать предпочтение композиции, а не наследованию.

Принцип подстановки Барбары Лисков (LSP)

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

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

Вот участок кода, который противоречит LSP.

какой принцип ооп нарушает следующий фрагмент кода

Метод area(Rectangle r) просчитывает площадь Rectangle. Программа упадет после выполнения Square, поскольку Square здесь не является Rectangle. Согласно принципу LSP, функции, которые используют ссылки на базовые классы, должны иметь возможность использовать и объекты производных классов без дополнительных инструкций.

Этот принцип, который является специфичным определением подтипа, был предложен Барбарой Лисков в 1987 году на конференции в основном докладе под названием «Абстракция данных и иерархия» — отсюда и его название.

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

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

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

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

Достоинством принципа ISP в Java является то, что сначала нужно реализовать все методы, и только потом они могут быть использованы классами. Поэтому принцип дает возможность снизить количество методов.

какой принцип ооп нарушает следующий фрагмент кода

Программирование для интерфейса, а не реализации

Здесь все понятно из названия. Применение этого принципа ведет к созданию гибкого кода, который сможет работать с любой новой реализацией интерфейса.

Следует использовать тип интерфейса для переменных, возвращаемых типов или же типа аргумента метода. Пример — использование SuperClass, а не SubClass.

List numbers= getNumbers();

ArrayList numbers = getNumbers();

Вот практическая реализация того, о чем говорится выше.

какой принцип ооп нарушает следующий фрагмент кода

Принцип делегирования

Распространенный пример — методы equals() и hashCode() в Java. Когда требуется сравнить два объекта, то это действие делегируется соответствующему классу вместо клиентского.

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

какой принцип ооп нарушает следующий фрагмент кода

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

Источник

В какой строке будет ошибка при компиляции данного кода?

Выберите свойства конструктора.

Конструктор вызывается при создании объекта

Конструктор может иметь любое имя

Конструктор имеет имя, совпадающее с именем класса

Конструктор может возвращать любое значение, совпадающее с типом возвращаемого значения, определенного в заголовке функции

Конструктор не возвращает значения

При отсутствии конструктора в определении класса, компилятор создает конструктор по умолчанию

При отсутствии конструктора копирования в определении класса, компилятор создает его автоматически

Определите особенности динамического выделения памяти с помощью оператора new.

Не нужно использовать оператор sizeof для вычисления размера выделяемой памяти

Не нужно использовать операцию приведения типов

Для корректной работы необходимо выполнить операцию приведения типов

Можно перегружать оператор new

Невозможно инициализировать данные при динамическом выделении памяти с помощью оператора new

Для использования оператора new необходимо подключить соответствующую библиотеку с помощью директивы #include

Выполняется инициализация объекта, для которого динамически выделяется память с помощью оператора new

3. Какие операции нельзя перегружать в C++?

4. Правило использования перегруженных бинарных операций следующее:

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

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

5. Наследование классов на языке C++ задается следующим образом:

class имя_производного_класса (имя_базового класса) < тело_класса >;

class спецификатор_доступа имя_производного_класса, имя_базового_класса < тело_класса >;

class имя_производного_класса : спецификатор_доступа имя_базового_класса < тело_класса >;

6. Имеется ли доступ к членам класса из производного класса при использовании различных спецификаторов доступа в наследуемом классе:

publicнет
protectedесть
private

Ответ:

publicесть
protectedесть
privateнет

7. Имеется ли доступ к членам класса из внешних функций при использовании различных спецификаторов доступа в классе:

publicнет
protectedесть
private

Ответ:

publicесть
protectedнет
privateнет

8. Имеется ли доступ к членам класса из самого класса при использовании различных спецификаторов доступа:

publicесть
protectedнет
private

Ответ:

publicесть
protectedесть
privateесть

9. Правило вызова конструкторов и деструкторов при наследовании:

деструкторы выполняются в порядке наследования, а конструкторы в обратном порядке

конструкторы выполняются в порядке наследования, а деструкторы в обратном порядке

выполняются только конструкторы и деструкторы производного класса

конструкторы и деструкторы выполняются в порядке наследования

это метод класса, объявленный с ключевым словом friend

это функция, не принадлежащая классу, но имеющая доступ к данным класса

это функция, описанная в классе с ключевым словом friend, но не имеющая доступ к данным класса

метод, который нельзя переопределить в производных классах

метод, который вызывается не для объектов класса, а для класса, и выполняет действия, относящиеся не к объекту, а к классу

Назначение инструкции throw.

Это инструкция, которая обрабатывает исключительную ситуацию, возникшую в блоке try.

Это инструкция, которая возбуждает исключительную ситуацию.

Описывает блок кода, в котором может произойти обрабатываемая исключительная ситуация.

13. В объектно-ориентированных языках программирования полиморфизм обеспечивается с помощью:

Передачи аргументов по ссылке

Ограничения доступа к полям и методам

14. Какой принцип ООП нарушает следующий фрагмент кода:

15. Выберете наиболее точное определение наследования:

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

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

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

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

это механизм, который позволяет описывать новые классы на основании других классов

Как называется способность объекта скрывать свои данные и реализацию от других объектов системы?

17. Какой принцип ООП необходимо использовать, чтобы заменить конструкции if-then-else в данном фрагменте кода:

18. Выберете наиболее точное определение полиморфизма:

это принцип, согласно которому объекты, имеющие одинаковый интерфейс, могут вести себя по-разному

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

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

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

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

Корректен ли следующий код?

20. Что напечатает следующий код при создании экземпляра класса X:

21. Что выведет следующий код:

Произойдет ошибка компиляции

Ошибка времени выполнения

22. Что напечатает следующий код:

возникнет ошибка компиляции

возникнет ошибка выполнения

23. Какие независимые друг от друга изменения позволят коду отработать корректно:

изменить public на private в строке 1

изменить строку 2 на код: void Count()

добавить в строку 3 код: void Count()

добавить в строку 3 код: void Counter::Count()

В какой строке будет ошибка при компиляции данного кода?

1 (невозможно объявление статических членов класса)

2 (невозможно объявление статических функций класса)

3 (невозможно обращение к нестатическим членам класса из статических методов класса)

4 (невозможно обращение к статическим членам класса из методов класса)

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *