Принципы SOLID на примерах
Всем привет! Данная статья — эта попытка объяснить принципы SOLID на примерах пcевдокода на Java. Статья будет полезна начинающим разработчикам понять данные принципы проектирования.
Вначале рассмотрим общее понятие, что такое SOLID и как расшифровывается каждая буква данной аббревиатуры.
SOLID — это принципы разработки программного обеспечения, следуя которым Вы получите хороший код, который в дальнейшем будет хорошо масштабироваться и поддерживаться в рабочем состоянии.
S — Single Responsibility Principle — принцип единственной ответственности. Каждый класс должен иметь только одну зону ответственности.
O — Open closed Principle — принцип открытости-закрытости. Классы должны быть открыты для расширения, но закрыты для изменения.
L — Liskov substitution Principle — принцип подстановки Барбары Лисков. Должна быть возможность вместо базового (родительского) типа (класса) подставить любой его подтип (класс-наследник), при этом работа программы не должна измениться.
I — Interface Segregation Principle — принцип разделения интерфейсов. Данный принцип обозначает, что не нужно заставлять клиента (класс) реализовывать интерфейс, который не имеет к нему отношения.
D — Dependency Inversion Principle — принцип инверсии зависимостей. Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те, и другие должны зависеть от абстракции. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Рассмотрим первый принцип — принцип единственной ответственности на примере.
Допустим у нас есть класс RentCarService и в нем есть несколько методов: найти машину по номеру, забронировать машину, распечатать заказ, получить информацию о машине, отправить сообщение.
public class RentCarService < public Car findCar(String carNo) < //find car by number return car; >public Order orderCar(String carNo, Client client) < //client order car return order; >public void printOrder(Order order) < //print order >public void getCarInterestInfo(String carType) < if (carType.equals("sedan")) < //do some job >if (carType.equals("pickup")) < //do some job >if (carType.equals("van")) < //do some job >> public void sendMessage(String typeMessage, String message) < if (typeMessage.equals("email")) < //write email //use JavaMailSenderAPI >> >
У данного класса есть несколько зон ответственности, что является нарушением первого принципа. Возьмем метод получения информации об машине. Теперь у нас есть только три типа машин sedan, pickup и van, но если Заказчик захочет добавить еще несколько типов, тогда придется изменять и дописывать данный метод.
Или возьмем метод отправки сообщения. Если кроме отправки сообщения по электронной почте необходимо будет добавить отправку смс, то также необходимо будет изменять данный метод.
Одним словом, данный класс нарушает принцип единой ответственности, так как отвечает за разные действия.
Необходимо разделить данный класс RentCarService на несколько, и тем самым, следуя принципу единой ответственности, предоставить каждому классу отвечать только за одну зону или действие, так в дальнейшем его будет проще дополнять и модифицировать.
Необходимо создать класс PrinterService и вынести там функционал по печати.
public class PrinterService < public void printOrder(Order order) < //print order >>
Аналогично работа связанная с поиском информации о машине перенести в класс CarInfoService.
public class CarInfoService < public void getCarInterestInfo(String carType) < if (carType.equals("sedan")) < //do some job >if (carType.equals("pickup")) < //do some job >if (carType.equals("van")) < //do some job >> >
Метод по отправке сообщений перенести в класс NotificationService.
public class NotificationService < public void sendMessage(String typeMessage, String message) < if (typeMessage.equals("email")) < //write email //use JavaMailSenderAPI >> >
А метод поиска машины в CarService.
public class CarService < public Car findCar(String carNo) < //find car by number return car; >>
И в классе RentCarService останется только один метод.
public class RentCarService < public Order orderCar(String carNo, Client client) < //client order car return order; >>
Теперь каждый класс несет ответственность только за одну зону и есть только одна причина для его изменения.
Принцип открытости-закрытости рассмотрим на примере только что созданного класса по отправке сообщений.
public class NotificationService < public void sendMessage(String typeMessage, String message) < if (typeMessage.equals("email")) < //write email //use JavaMailSenderAPI >> >
Допустим нам необходимо кроме отправки сообщения по электронной почте отправлять еще смс сообщения. И мы можем дописать метод sendMessage таким образом:
public class NotificationService < public void sendMessage(String typeMessage, String message) < if (typeMessage.equals("email")) < //write email //use JavaMailSenderAPI >if (typeMessage.equals("sms")) < //write sms //send sms >> >
Но в данном случае мы нарушим второй принцип, потому что класс должен быть закрыт для модификации, но открыт для расширения, а мы модифицируем (изменяем) метод.
Для того чтобы придерживаться принципа открытости-закрытости нам необходимо спроектировать наш код таким образом, чтобы каждый мог повторно использовать нашу функцию, просто расширив ее. Поэтому создадим интерфейс NotificationService и в нем поместим метод sendMessage.
public interface NotificationService
Далее создадим класс EmailNotification, который имплементит интерфейс NotificationService и реализует метод отправки сообщений по электронной почте.
public class EmailNotification implements NotificationService < @Override public void sendMessage(String message) < //write email //use JavaMailSenderAPI >>
Создадим аналогично класс MobileNotification, который будет отвечать за отправку смс сообщений.
public class MobileNotification implements NotificationService < @Override public void sendMessage(String message) < //write sms //send sms >>
Проектируя таким образом код мы не будем нарушать принцип открытости-закрытости, так как мы расширяем нашу функциональность, а не изменяем (модифицируем) наш класс.
Давайте сейчас рассмотрим третий принцип: принцип подстановки Барбары Лисков.
Данный принцип непосредственно связан с наследованием классов. Допустим у нас есть базовый класс Счет (Account), в котором есть три метода: просмотр остатка на счете, пополнение счета и оплата.
public class Account < public BigDecimal balance(String numberAccount)< //logic return bigDecimal; >; public void refill(String numberAccount, BigDecimal sum) < //logic >public void payment(String numberAccount, BigDecimal sum) < //logic >>
Нам необходимо написать еще два класса: зарплатный счет и депозитный счет, при этом зарплатный счет должен поддерживать все операции, представленные в базовом классе, а депозитный счет — не должен поддерживать проведение оплаты.
public class SalaryAccount extends Account< @Override public BigDecimal balance(String numberAccount)< //logic return bigDecimal; >; @Override public void refill(String numberAccount, BigDecimal sum) < //logic >@Override public void payment(String numberAccount, BigDecimal sum) < //logic >>
public class DepositAccount extends Account< @Override public BigDecimal balance(String numberAccount)< //logic return bigDecimal; >; @Override public void refill(String numberAccount, BigDecimal sum) < //logic >@Override public void payment(String numberAccount, BigDecimal sum) < throw new UnsupportedOperationException("Operation not supported"); >>
Если сейчас в коде программы везде, где мы использовали класс Account заменить на его класс-наследник (подтип) SalaryAccount, то программа продолжит нормально работать, так как в классе SalaryAccount доступны все операции, которые есть и в классе Account.
Если же мы такое попробуем сделать с классом DepositAccount, то есть заменим базовый класс Account на его класс-наследник DepositAccount, то программа начнет неправильно работать, так как при вызове метода payment() будет выбрасываться исключение new UnsupportedOperationException. Таким образом произошло нарушение принципа подстановки Барбары Лисков.
Для того чтобы следовать принципу подстановки Барбары Лисков необходимо в базовый (родительский) класс выносить только общую логику, характерную для классов наследников, которые будут ее реализовывать и, соответственно, можно будет базовый класс без проблем заменить на его класс-наследник.
В нашем случае класс Account будет выглядеть следующим образом.
public class Account < public BigDecimal balance(String numberAccount)< //logic return bigDecimal; >; public void refill(String numberAccount, BigDecimal sum) < //logic >>
Мы сможем от него наследовать класс DepositAccount.
public class DepositAccount extends Account< @Override public BigDecimal balance(String numberAccount)< //logic return bigDecimal; >; @Override public void refill(String numberAccount, BigDecimal sum) < //logic >>
Создадим дополнительный класс PaymentAccount, который унаследуем от Account и его расширим методом проведения оплаты.
public class PaymentAccount extends Account < public void payment(String numberAccount, BigDecimal sum)< //logic >>
И наш класс SalaryAccount уже унаследуем от класса PaymentAccount.
public class SalaryAccount extends PaymentAccount< @Override public BigDecimal balance(String numberAccount)< //logic return bigDecimal; >; @Override public void refill(String numberAccount, BigDecimal sum) < //logic >@Override public void payment(String numberAccount, BigDecimal sum) < //logic >>
Сейчас замена класса PaymentAccount на его класс-наследник SalaryAccount не «поломает» нашу программу, так как класс SalaryAccount имеет доступ ко всем методам, что и PaymentAccount. Также все будет хорошо при замене класса Account на его класс-наследник PaymentAccount.
Принцип подстановки Барбары Лисков заключается в правильном использовании отношения наследования. Мы должны создавать наследников какого-либо базового класса тогда и только тогда, когда они собираются правильно реализовать его логику, не вызывая проблем при замене родителей на наследников.
Рассмотрим теперь принцип разделения интерфейсов.
Допустим у нас имеется интерфейс Payments и в нем есть три метода: оплата WebMoney, оплата банковской карточкой и оплата по номеру телефона.
public interface Payments
Далее нам надо реализовать два класса-сервиса, которые будут у себя реализовывать различные виды проведения оплат (класс InternetPaymentService и TerminalPaymentService). При этом TerminalPaymentService не будет поддерживать проведение оплат по номеру телефона. Но если мы оба класса имплементим от интерфейса Payments, то мы будем «заставлять» TerminalPaymentService реализовывать метод, который ему не нужен.
public class InternetPaymentService implements Payments < @Override public void payWebMoney() < //logic >@Override public void payCreditCard() < //logic >@Override public void payPhoneNumber() < //logic >>
public class TerminalPaymentService implements Payments < @Override public void payWebMoney() < //logic >@Override public void payCreditCard() < //logic >@Override public void payPhoneNumber() < //. >>
Таким образом произойдет нарушение принципа разделения интерфейсов.
Для того чтобы этого не происходило необходимо разделить наш исходный интерфейс Payments на несколько и, создавая классы, имплементить в них только те интерфейсы с методами, которые им нужны.
public interface WebMoneyPayment
public interface CreditCardPayment
public interface PhoneNumberPayment
public class InternetPaymentService implements WebMoneyPayment, CreditCardPayment, PhoneNumberPayment < @Override public void payWebMoney() < //logic >@Override public void payCreditCard() < //logic >@Override public void payPhoneNumber() < //logic >>
public class TerminalPaymentService implements WebMoneyPayment, CreditCardPayment < @Override public void payWebMoney() < //logic >@Override public void payCreditCard() < //logic >>
Давайте сейчас рассмотрим последний принцип: принцип инверсии зависимостей.
Допустим мы пишем приложение для магазина и решаем вопросы с проведением оплат. Вначале это просто небольшой магазин, где оплата происходит только за наличные. Создаем класс Cash и класс Shop.
public class Cash < public void doTransaction(BigDecimal amount)< //logic >>
public class Shop < private Cash cash; public Shop(Cash cash) < this.cash = cash; >public void doPayment(Object order, BigDecimal amount) < cash.doTransaction(amount); >>
Вроде все хорошо, но мы уже нарушили принцип инверсии зависимостей, так как мы тесно связали оплату наличными к нашему магазину. И если в дальнейшем нам необходимо будет добавить оплату еще банковской картой и телефоном («100% понадобится»), то нам придется переписывать и изменять много кода. Мы в нашем коде модуль верхнего уровня тесно связали с модулем нижнего уровня, а нужно чтобы оба уровня зависели от абстракции.
Поэтому создадим интерфейс Payments.
public interface Payments
Теперь все наши классы по оплате будут имплементить данный интерфейс.
public class Cash implements Payments < @Override public void doTransaction(BigDecimal amount) < //logic >>
public class BankCard implements Payments < @Override public void doTransaction(BigDecimal amount) < //logic >>
public class PayByPhone implements Payments < @Override public void doTransaction(BigDecimal amount) < //logic >>
Теперь надо перепроектировать реализацию нашего магазина.
public class Shop < private Payments payments; public Shop(Payments payments) < this.payments = payments; >public void doPayment(Object order, BigDecimal amount) < payments.doTransaction(amount); >>
Сейчас наш магазин слабо связан с системой оплаты, то есть он зависит от абстракции и уже не важно каким способом оплаты будут пользоваться (наличными, картой или телефоном) все будет работать.
Мы рассмотрели на примерах псевдокода принципы SOLID, надеюсь кому-то будет это полезно.
Спасибо Всем, кто дочитал до конца. Всем пока.
- принципы проектирования
- принципы разработки
- solid
SOLID — принципы объектно‑ориентированного программирования
SOLID — это аббревиатура пяти основных принципов проектирования в объектно‑ориентированном программировании — Single responsibility, Open-closed, Liskov substitution, Interface segregation и Dependency inversion.
В переводе на русский: принципы единственной ответственности, открытости / закрытости, подстановки Барбары Лисков, разделения интерфейса и инверсии зависимостей)
Аббревиатура SOLID была предложена Робертом Мартином, автором нескольких книг, широко известным в сообществе разработчиков. Следование принципам позволяет строить на базе ООП масштабируемые и сопровождаемые программные продукты с понятной бизнес‑логикой. Код, который написан с соблюдением принципов SOLID, проще понимать, поддерживать, расширять или изменять его функциональность.
Расшифровка:
- Single responsibility — принцип единственной ответственности
- Open-closed — принцип открытости / закрытости
- Liskov substitution — принцип подстановки Барбары Лисков
- Interface segregation — принцип разделения интерфейса
- Dependency inversion — принцип инверсии зависимостей
Принцип единственной обязанности / ответственности (single responsibility principle / SRP) обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности. Подробнее про SRP →
Принцип открытости / закрытости (open-closed principle / OCP) декларирует, что программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что эти сущности могут менять свое поведение без изменения их исходного кода. Подробнее про OCP →
Принцип подстановки Барбары Лисков (Liskov substitution principle / LSP) в формулировке Роберта Мартина: «функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом». Подробнее про LSP →
Принцип разделения интерфейса (interface segregation principle / ISP) в формулировке Роберта Мартина: «клиенты не должны зависеть от методов, которые они не используют». Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют. Подробнее про ISP →
Принцип инверсии зависимостей (dependency inversion principle / DIP) — модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций. Подробнее про DIP →
Статья опубликована в 2019 и была обновлена в 2023 году
Тематические статьи
Принцип программирования YAGNI — «Вам это не понадобится»
Принцип заключается в том, что возможности, которые не описаны в требованиях к системе, просто не должны реализовываться.
В результате разработка ненужных функций не сжигает бюджет проекта, а разработчики не тратят оплачиваемое время на реализацию и дальнейшее сопровождение в реальности ненужного функционала. Избыточный функционал сжигает больше всего ресурсов именно на сопровождении: больше написанного кода — труднее сопровождать и выше вероятность появления «багов». И тут очень уместна поговорка: «лучший код — это ненаписанный код».
методологии разработки
веб-разработка
Статья опубликована в 2019 и обновлена в 2023 году
Принцип программирования KISS — делайте вещи проще
KISS — это принцип проектирования и программирования, при котором простота системы декларируется в качестве основной цели или ценности.
Большая часть программных систем необосновано перегружена практически ненужными функциями, что ухудшает удобство их использование конечными пользователями, а также усложняет их поддержку и развитие разработчиками. Следование принципу KISS позволяет разрабатывать решения, которые не обладают этими недостатками: они просты в использовании и в сопровождении.
методологии разработки
веб-разработка
Статья опубликована в 2019 и обновлена в 2023 году
Принцип программирования DRY — don’t repeat yourself / не повторяйте себя
Следование принципу DRY позволяет добиться высокой сопровождаемости программного продукта: внесение изменений и тестирование значительно упрощаются.
Если код не дублируется, то для изменения логики достаточно внесения исправлений всего в одном месте. Также значительно проще тестировать одну (пусть и более сложную) функцию, а не набор из десятков однотипных. При следовании DRY упрощается и повторное использование функций, вынесенных из сложных алгоритмов, что позволяет сократить время разработки и тестирования новой функциональности.
веб-разработка
методологии разработки
Статья опубликована в 2018 и обновлена в 2023 году
Стандарты кодирования — залог хорошей сопровождаемости проекта
Любая командная разработка может быть эффективной только в том случае, если участники команды имеют общее видение.
Если над проектом работает команда, а не один‑два разработчика, то обязательно должен быть стандарт оформления кода — набор правил и соглашений, которые описывают базовые принципы оформления программного кода, используемого совместно группой разработчиков.
методологии разработки
веб-разработка
Статья опубликована в 2014 и обновлена в 2023 году
Флаги функций (Feature Flags)
Флаги функций позволяют отделить развертывание функций от развертывания кода, обеспечивают возможности для A/B-тестирования и предоставляют механизм быстрого отключения проблемных функций
СОЛИД
византийская золотая монета весом около 4,5 г, чеканившаяся в IV в. н.э.
Райзберг Б.А., Лозовский Л.Ш., Стародубцева Е.Б. . Современный экономический словарь. — 2-е изд., испр. М.: ИНФРА-М. 479 с. . 1999 .
Экономический словарь . 2000 .
Синонимы:
Смотреть что такое «СОЛИД» в других словарях:
- СОЛИД — (лат. solidus букв. прочный, массивный), римская, позднее византийская золотая монета, стала чеканиться в 309. Солид был заимствован у Рима германскими народами и послужил образцом золотых монет раннего средневековья в Зап. Европе … Большой Энциклопедический словарь
- Солид — византийская золотая монета весом около 4.5 г, чеканилась в 6 веке н.э. См. также: Золотые старинные монеты Византия Финансовый словарь Финам … Финансовый словарь
- СОЛИД — золотая монета, род дуката во времена Константина Великого = ок. 3 руб., и серебряная (solidus argenteus) счетная монета во времена меровингов. Словарь иностранных слов, вошедших в состав русского языка. Чудинов А.Н., 1910 … Словарь иностранных слов русского языка
- солид — сущ., кол во синонимов: 1 • монета (298) Словарь синонимов ASIS. В.Н. Тришин. 2013 … Словарь синонимов
- солид — * solide adj. Основательный, прочный. Это показывает, сколь велики ресурсы Франции и сколь прочны (solides) источники ее дохода. 21. 10. 1842. А. И. Тургенев П. Вяземскому. Солидное (solide) вино: хорошо составленное и крепко сбитое. Как правило … Исторический словарь галлицизмов русского языка
- Солид — У этого термина существуют и другие значения, см. Солидус (значения). Солид (Римская империя) Номинал … Википедия
- солид — (лат. solidus, буквально прочный, массивный), римская, позднее византийская золотая монета, стала чеканиться в 309. Солид был заимствован у Рима германскими народами и послужил образцом золотых монет раннего средневековья в Западной Европе. * *… … Энциклопедический словарь
- Солид — (от лат. solidus крепкий, прочный, массивный) 1) Золотая монета Рим. империи, Византии, ранне феод. гос в эпохи Вел. переселения народов, Франкского гос ва весом от 3,88 до 4,55 г, начала чеканиться с 309. 2) В 1759 61 серебр. С. чеканились… … Российский гуманитарный энциклопедический словарь
- Солид Снейк — англ. Solid Snake Солид Снейк в Metal Gear Solid 2 Появление Metal Gear … Википедия
- Солид — Золотая римская монета, которую ввел в обращение император Константин 1 … Словарь нумизмата
- Обратная связь: Техподдержка, Реклама на сайте
- Путешествия
Экспорт словарей на сайты, сделанные на PHP,
WordPress, MODx.
- Пометить текст и поделитьсяИскать в этом же словареИскать синонимы
- Искать во всех словарях
- Искать в переводах
- Искать в ИнтернетеИскать в этой же категории
Поделиться ссылкой на выделенное
Прямая ссылка:
… Нажмите правой клавишей мыши и выберите «Копировать ссылку»
SOLID-принципы: что такое и зачем нужны. Разбираем по буквам
Спойлер: к игре Metal Gear Solid пять принципов объектно-ориентированного программирования отношения не имеют.
SOLID – это какая-то игра?
Не совсем. SOLID – пять принципов объектно-ориентированного программирования, которые задают архитектуру программы.
Разберем по буквам:
S (The Single Responsibility Principle) – принцип единой ответственности, то есть один класс решает одну задачу и у класса должна быть только одна причина для изменения. Если класс задает направление движения машины, то этот класс не должен выполнять какие-либо другие задачи. Таким образом, данный принцип помогает разбивать общую конструкцию на независимые модули и уменьшать межмодульную связью.
O (The Open Closed Principle) – принцип открытости/замкнутости. Если понадобилось добавить новую функциональность к классу, то существующий класс не модифицируем, а создаем наследника класса с новыми возможностями. То есть у нас должна быть возможность расширять класс без изменения самого класса.
L (The Liskov Substitution Principle) – принцип подстановки Лисков, описывающий возможности заменяемости экземпляров объектов. Простыми словами: дочерний класс должен следовать принципам родительского класса и не изменять их. Пусть у нас есть класс Прямоугольник с методами, задающими ширину, высоту и рассчитывающим площадь. Теперь мы захотели создать класс Квадрат . Квадрат – тот же самый прямоугольник, но с одинаковыми сторонами. Класс Квадрат наследуется от класса Прямоугольник и переопределяет его методы: подставляем значения – все работает. Но если мы начнем использовать класс Прямоугольник в качестве интерфейса, а работать будем с классом Квадрат , мы разом изменяем оба параметра. Чтобы решить эту проблему, создается общий интерфейс для обоих классов и вместо наследования одного класса от другого использовать этот самый интерфейс.
I (The Interface Segregation Principle) – принцип разделения интерфейсов. Создавайте узкоспециализированные интерфейсы и не вынуждайте клиента зависеть от неиспользуемых интерфейсов. Допустим есть класс Auto с методами комплектаций для всех автомобилей. Если мы наследуемся от интерфейса, то все методы реализованные в нем должны быть описаны в классе-потомке. В результате чего классы могут получить ненужные методы. Для решения этой проблемы мы разделяем интерфейсы.
D (The Dependency Inversion Principle) – принцип инверсии зависимостей. Сущности должны зависеть от абстракций, а не от чего-то конкретного. Допустим, у нас есть низкоуровневый класс HTTPService с логикой запроса и высокоуровневый класс HTTP , в конструктор которого мы передаем низкоуровневый модуль. После чего вызываем его методы и нарушаем принцип инверсии зависимости: высокоуровневый модель зависит от низкоуровневого. Для решения проблемы мы создаем отдельный интерфейс и передаем его в высокоуровневый интерфейс. Теперь наш класс не зависит от низкоуровневого модуля.
Я так ничего и не понял, можно объяснить доступно?
Конечно. 20 января мы провели бесплатный вебинар «Простой рабочий алгоритм использования SOLID на практике», на котором подробно рассказали о принципах SOLID. Вот запись вебинара:
Слова, слова, слова… SOLID-принципы нужны, чтобы почувствовать себя умным? Какой профит?
SOLID-принципы нужны, чтобы быть образованным. Знание аббревиатур и понимание смыслов, стоящих за ними, расширяет инструментарий разработчика и делает его конкурентноспособным.
Чем дальше в лес, тем больше дров
Помимо SOLID-принципов, разрабу пригодятся паттерны проектирования, тестирование, виды сложности, абстракции и многое другое.
Кстати, 15 февраля стартует наш курс «Архитектуры и шаблоны проектирования» , на котором вы научитесь:
- строить архитектуры приложений, которые позволяют не снижать скорость разработки по мере развития проекта;
- писать модульные тесты на Mock-объектах;
- применять SOLID принципы не только в объектно-ориентированных языках;
- использовать CI и IoC контейнеры.
Что нужно для старта?
Для старта достаточно знать любой объектно-ориентированный язык программирования: Python, Java, PHP, C++, JavaScript, C# и др.
Игра стоит свеч?
Да, безусловно. Фундаментальные знания на земле не валяются.