[.NET, Проектирование и рефакторинг, C#, Профессиональная литература] Книга «Внедрение зависимостей на платформе .NET. 2-е издание»
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
оригинал
Привет, Хаброжители! Парадигма внедрения зависимостей (DI) в течение минувшего десятилетия де-факто стала одной из доминирующих на платформе .NET и теперь обязательна к изучению для всех .NET-разработчиков.
Это переработанное и дополненное издание классической книги «Внедрение зависимостей в .NET». Вы научитесь правильно внедрять зависимости для устранения жесткой связи между компонентами приложения. Познакомитесь с подробными примерами и усвоите основы работы с ключевыми библиотеками, необходимыми для внедрения зависимостей в .NET и .NET Core.
В книге:
• Рефакторинг существующего кода в слабо связанный код
• Методы DI, которые работают со статически типизированными ОО языками
• Интеграция с общими платформами .NET Framework
• Обновленные примеры, иллюстрирующие DI в .NET Core
Антипаттерны внедрения зависимостей
В этой главе
- Создание кода с сильной связанностью при использовании антипаттерна «Диктатор» (Control Freak).
- Запрашивание зависимостей класса с применением антипаттерна «Локатор сервисов» (Service Locator).
- Придание нестабильной зависимости глобальной доступности при использовании антипаттерна «Окружающий контекст» (Ambient Context).
- Принудительное использование конкретной сигнатуры конструктора с применением антипаттерна «Ограниченная конструкция» (Constrained Construction).
Многие блюда готовятся на сковороде с маслом. Если рецепт еще не освоен, можно начать прогревать масло, отвернуться от сковороды и вчитываться в рецепт. Но, как только завершится нарезка овощей, масло начнет подгорать. Можно подумать, что дымящееся масло означает, что сковорода нагрелась и готова к приготовлению блюда. Это обычное заблуждение неопытных поваров. Когда масло начинает подгорать, оно также начинает разлагаться. Это называется достижением температуры дымообразования. Большинство подгоревших масел приобретают неприятный вкус, но это еще не все. Они образуют вредные соединения и теряют полезные антиоксиданты.
В предыдущей главе приводилось краткое сравнение паттернов проектирования с рецептами. Паттерн предоставляет понятный всем язык, которым можно воспользоваться для конструктивного обсуждения сложной концепции.
Когда концепция (или, скорее, реализация) искажается, у нас получается антипаттерн.
ОПРЕДЕЛЕНИЕ
Под антипаттерном понимается часто встречающееся решение проблемы, порождающее явные негативные последствия, хотя наряду с ним существуют и другие доступные задокументированные решения, доказавшие свою эффективность.
Перегревшееся задымившее масло — типичный пример, подпадающий под понятие кулинарного антипаттерна. Это весьма распространенная ошибка. Ее допускают многие неопытные повара, поскольку им кажется, что так и нужно делать, а негативные последствия выражаются в том, что еда теряет вкус и становится вредной.
Антипаттерны в некотором смысле являются формализованным способом описания распространенных ошибок, неоднократно допускаемых людьми. В этой главе будет рассмотрен ряд наиболее распространенных антипаттернов, связанных с внедрением зависимостей. Все они в той или иной форме попадались нам в ходе профессиональной деятельности, да мы и сами порой грешили их применением.
Зачастую антипаттерны являются простыми попытками реализации DI в приложении. Но из-за своего неполного соответствия основам DI-реализации могут превратиться в решения, приносящие больше вреда, чем пользы. Изучение этих антипаттернов может дать вам представление о том, какие ловушки следует обходить при реализации своих первых проектов с применением DI. Но даже при многолетнем опыте применения DI можно запросто ошибиться.
ВНИМАНИЕ
Эта глава отличается от других глав, поскольку основной объем демонстрируемого кода является примером того, как не следует выполнять реализацию DI. Не пытайтесь повторять это на практике!
Исправления в антипаттерны могут быть внесены путем реструктуризации кода в направлении одного из паттернов DI, представленного в главе 4. Насколько сложно будет выполнить исправление в каждом отдельно взятом случае, зависит от особенностей реализации. Для каждого антипаттерна будут предоставлены некие обобщенные рекомендации по его реструктуризации в целях получения наиболее подходящего паттерна.
ПРИМЕЧАНИЕ
Поскольку данная тема не является для этой книги главной, реструктуризация от антипаттерна DI к паттерну будет рассмотрена только в настоящей главе. Если есть заинтересованность в углубленном изучении возможностей переработки уже существующих приложений в сторону использования технологии DI, то этому посвящена целая книга: Feathers M. C. Working Effectively with Legacy Code. — Prentice Hall, 2004. Хотя речь в ней идет не только о DI, она охватывает большинство понятий, изучаемых в данной книге.
Для придания ему тестируемости иногда устаревший код требует радикальных мер. Зачастую это означает постепенное внесение изменений, позволяющих избежать случайного вывода из строя ранее работающего приложения. В некоторых случаях наиболее подходящим временным решением может стать применение антипаттерна. Несмотря на то что такой шаг может оказаться улучшением по сравнению с исходным кодом, важно отметить, что от этого он не становится паттерном; существуют и другие задокументированные и повторяемые решения, доказавшие свою эффективность. Антипаттерны, рассматриваемые в этой главе, сведены в табл. 5.1.
Остальная часть главы описывает каждый антипаттерн, представляя их в порядке важности. Читать главу можно последовательно или изучать только то, что представляет интерес, поскольку разделы не связаны друг с другом. Если читать все подряд не захочется, то советуем обратиться к соответствующим разделам об антипаттернах «Диктатор» и «Локатор сервисов».
Наиболее важным считается паттерн внедрения зависимостей через конструктор, а в качестве наиболее часто встречающегося антипаттерна фигурирует «Диктатор». Он сильно мешает применению любой правильной технологии DI, поэтому, прежде чем приступать к изучению других антипаттернов, предлагаем сфокусировать внимание именно на нем. Но наибольшую опасность представляет антипаттерн «Локатор сервисов», поскольку в нем зачастую видят решение проблемы, создаваемой антипаттерном «Диктатор». Он будет рассмотрен в разделе 5.2.
5.1. Антипаттерн «Диктатор»
Что противоположно инверсии управления? Изначально понятие «инверсия управления» было придумано для противопоставления обычному положению дел, но мы не можем вести речь об антипаттерне «обычного порядка». Вместо этого для описания класса, не отказывающегося от управления своими нестабильными зависимостями, применим термин «Диктатор».
ОПРЕДЕЛЕНИЕ
Антипаттерн «Диктатор» всегда формируется в любом месте, кроме корня композиции, где код полагается на применение нестабильной зависимости. Он нарушает принцип инверсии зависимостей, рассмотренный в подразделе 3.1.2.
Антипаттерн «Диктатор» формируется, к примеру, при создании нового экземпляра нестабильной зависимости с помощью ключевого слова new. Реализация антипаттерна «Диктатор» показана в листинге 5.1.
При каждом создании нестабильной зависимости получается неявное утверждение о намерении управлять жизненным циклом экземпляра и о том, что никто другой не получит возможности перехватывать данный конкретный объект. Хотя использование ключевого слова new в отношении нестабильной зависимости — пример проблемного кода, к его применению в отношении стабильных зависимостей это не относится.
ПРИМЕЧАНИЕ
Не нужно думать, что ключевое слово new вдруг стало «незаконным», но от его использования для получения нестабильной зависимости вне корня композиции нужно воздерживаться. К тому же не следует забывать и о статических классах, которые также могут стать нестабильными зависимостями. Хотя вы никогда не будете использовать ключевое слово new в отношении этих классов, зависимость от этих классов вызывает те же самые проблемы.
Наиболее явно антипаттерн «Диктатор» проявляется, когда не предпринимается никаких усилий для ввода в код каких-либо абстракций. Несколько соответствующих примеров вы уже видели в главе 2, когда Мэри создавала свое приложение электронной торговли (см. раздел 2.1). Попытка ввода DI при таком подходе не делается. Но даже там, где разработчики что-то слышали о технологии DI и о возможности создания компоновки, антипаттерн «Диктатор» зачастую в той или иной вариации все же встречается.
В следующих подразделах будет показано несколько примеров, напоминающих код, встречавшийся нам на практике. В каждом из рассматриваемых случаев разработчики старались улучшить интерфейсы, но при этом не разбирались как следует в исходных намерениях и мотивах.
5.1.1. Пример: антипаттерн «Диктатор», полученный путем обновления зависимостей с применением ключевого слова new
Многие разработчики слышали о принципе программирования для интерфейсов, но не понимали заложенного в него глубокого смысла. Пытаясь все сделать правильно или придерживаться передового опыта, они создавали код, не имеющий особого смысла. Так, в листинге 3.9 был показан вариант ProductService, использующий для извлечения предлагаемых товаров экземпляр интерфейса IProductRepository. Напомним, как выглядел соответствующий код:
public IEnumerable<DiscountedProduct> GetFeaturedProducts()
{
return
from product in this.repository.GetFeaturedProducts()
select product.ApplyDiscountFor(this.userContext);
}
Здесь важно то, что компонентная переменная repository представляет собой абстракцию. В главе 3 вы видели, как поле repository может быть заполнено внедрением через конструктор, но нам встречались и другие, более примитивные попытки. Одна из них приводится в листинге 5.2.
Поле repository объявлено через интерфейс IProductRepository, поэтому любой компонент в классе ProductService (например, GetFeaturedProducts) программируется для интерфейса. Хотя все это представляется правильным, из этого мало что получается, поскольку во время выполнения программы типом всегда будет SqlProductRepository. Способов перехвата или изменения переменной repository нет, если только не изменить и не перекомпилировать сам код. Кроме того, если жестко запрограммировать переменную так, что у нее всегда будет конкретный тип, то из ее объявления абстракцией ничего путного не получится. Непосредственное создание зависимостей с применением ключевого слова new является одним из примеров антипаттерна «Диктатор».
Прежде чем перейти к анализу и возможным способам решения возникающих проблем, порождаемых применением антипаттерна «Диктатор», рассмотрим еще несколько примеров для получения более четкого представления о контексте и общих чертах неудачных попыток применения технологии DI. В следующем примере сразу просматривается неудачное решение. Большинство разработчиков попытается усовершенствовать свой подход.
Об авторах
Стивен ван Дерсен (Steven van Deursen) — фрилансер из Нидерландов, занимающийся разработками и проектированием в среде .NET и получивший богатый опыт работы в этой сфере начиная с 2002 года. Он живет в Неймегене, занимается программированием и зарабатывает этим на жизнь. Помимо программирования Стивен увлекается боевыми искусствами, любит поесть и, конечно же, выпить хорошего виски.
Марк Симан (Mark Seemann) — программист, проектировщик программных средств и лектор, живет в столице Дании Копенгагене. Разработкой программных средств он занимается с 1995 года, а технологией TDD увлекается с 2003-го, в том числе шесть лет был консультантом, разработчиком и проектировщиком в компании Microsoft. В настоящий момент Марк занимается разработкой ПО. Он увлекается чтением, рисованием, игрой на гитаре, любит хорошее вино и изысканную еду.
Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — .NET
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование] Трудности перевода
- [.NET, Visual Studio, Отладка, Разработка под Windows] Гарантированная локализация/русификация консоли Windows
- [.NET, C#, Разработка под Windows] Пишем установщик на WixSharp. Плюшки, проблемы, возможности
- [Программирование, Анализ и проектирование систем, Проектирование и рефакторинг] Погружение в CQRS (перевод)
- [Программирование, Профессиональная литература, Читальный зал, Копирайт] Андрей Столяров выложил в свободный доступ второе издание книги «Программирование: введение в профессию»
- [Программирование, .NET, C#, Параллельное программирование] Многопоточность на низком уровне
- [Java, .NET, Go, Kubernetes] Мониторинг производительности приложений в Broadcom DX APM — анонс вебинара
- [Программирование, Assembler] Как писать на ассемблере в 2021 году
- [C#, TypeScript, Искусственный интеллект] Змейка, мышь и Гамильтон
- [Профессиональная литература, Читальный зал, Управление медиа, Копирайт, Научно-популярное] В открытом доступе опубликованы 40 самых важных научно-популярных книг на русском языке
Теги для поиска: #_.net, #_proektirovanie_i_refaktoring (Проектирование и рефакторинг), #_c#, #_professionalnaja_literatura (Профессиональная литература), #_.net, [url=https://torrents-local.xyz/search.php?nm=%23_blog_kompanii_izdatelskij_dom_«piter»&to=0&allw=0&o=1&s=0&f%5B%5D=820&f%5B%5D=959&f%5B%5D=958&f%5B%5D=872&f%5B%5D=967&f%5B%5D=954&f%5B%5D=885&f%5B%5D=882&f%5B%5D=863&f%5B%5D=881&f%5B%5D=860&f%5B%5D=884&f%5B%5D=865&f%5B%5D=873&f%5B%5D=861&f%5B%5D=864&f%5B%5D=883&f%5B%5D=957&f%5B%5D=859&f%5B%5D=966&f%5B%5D=956&f%5B%5D=955]#_blog_kompanii_izdatelskij_dom_«piter» (
Блог компании Издательский дом «Питер»
)[/url], #_.net, #_proektirovanie_i_refaktoring (
Проектирование и рефакторинг
), #_c#, #_professionalnaja_literatura (
Профессиональная литература
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 08:23
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
оригинал Привет, Хаброжители! Парадигма внедрения зависимостей (DI) в течение минувшего десятилетия де-факто стала одной из доминирующих на платформе .NET и теперь обязательна к изучению для всех .NET-разработчиков. Это переработанное и дополненное издание классической книги «Внедрение зависимостей в .NET». Вы научитесь правильно внедрять зависимости для устранения жесткой связи между компонентами приложения. Познакомитесь с подробными примерами и усвоите основы работы с ключевыми библиотеками, необходимыми для внедрения зависимостей в .NET и .NET Core. В книге: • Рефакторинг существующего кода в слабо связанный код • Методы DI, которые работают со статически типизированными ОО языками • Интеграция с общими платформами .NET Framework • Обновленные примеры, иллюстрирующие DI в .NET Core Антипаттерны внедрения зависимостей В этой главе
Многие блюда готовятся на сковороде с маслом. Если рецепт еще не освоен, можно начать прогревать масло, отвернуться от сковороды и вчитываться в рецепт. Но, как только завершится нарезка овощей, масло начнет подгорать. Можно подумать, что дымящееся масло означает, что сковорода нагрелась и готова к приготовлению блюда. Это обычное заблуждение неопытных поваров. Когда масло начинает подгорать, оно также начинает разлагаться. Это называется достижением температуры дымообразования. Большинство подгоревших масел приобретают неприятный вкус, но это еще не все. Они образуют вредные соединения и теряют полезные антиоксиданты. В предыдущей главе приводилось краткое сравнение паттернов проектирования с рецептами. Паттерн предоставляет понятный всем язык, которым можно воспользоваться для конструктивного обсуждения сложной концепции. Когда концепция (или, скорее, реализация) искажается, у нас получается антипаттерн. ОПРЕДЕЛЕНИЕ
Под антипаттерном понимается часто встречающееся решение проблемы, порождающее явные негативные последствия, хотя наряду с ним существуют и другие доступные задокументированные решения, доказавшие свою эффективность. Перегревшееся задымившее масло — типичный пример, подпадающий под понятие кулинарного антипаттерна. Это весьма распространенная ошибка. Ее допускают многие неопытные повара, поскольку им кажется, что так и нужно делать, а негативные последствия выражаются в том, что еда теряет вкус и становится вредной. Антипаттерны в некотором смысле являются формализованным способом описания распространенных ошибок, неоднократно допускаемых людьми. В этой главе будет рассмотрен ряд наиболее распространенных антипаттернов, связанных с внедрением зависимостей. Все они в той или иной форме попадались нам в ходе профессиональной деятельности, да мы и сами порой грешили их применением. Зачастую антипаттерны являются простыми попытками реализации DI в приложении. Но из-за своего неполного соответствия основам DI-реализации могут превратиться в решения, приносящие больше вреда, чем пользы. Изучение этих антипаттернов может дать вам представление о том, какие ловушки следует обходить при реализации своих первых проектов с применением DI. Но даже при многолетнем опыте применения DI можно запросто ошибиться. ВНИМАНИЕ
Эта глава отличается от других глав, поскольку основной объем демонстрируемого кода является примером того, как не следует выполнять реализацию DI. Не пытайтесь повторять это на практике! Исправления в антипаттерны могут быть внесены путем реструктуризации кода в направлении одного из паттернов DI, представленного в главе 4. Насколько сложно будет выполнить исправление в каждом отдельно взятом случае, зависит от особенностей реализации. Для каждого антипаттерна будут предоставлены некие обобщенные рекомендации по его реструктуризации в целях получения наиболее подходящего паттерна. ПРИМЕЧАНИЕ
Поскольку данная тема не является для этой книги главной, реструктуризация от антипаттерна DI к паттерну будет рассмотрена только в настоящей главе. Если есть заинтересованность в углубленном изучении возможностей переработки уже существующих приложений в сторону использования технологии DI, то этому посвящена целая книга: Feathers M. C. Working Effectively with Legacy Code. — Prentice Hall, 2004. Хотя речь в ней идет не только о DI, она охватывает большинство понятий, изучаемых в данной книге. Для придания ему тестируемости иногда устаревший код требует радикальных мер. Зачастую это означает постепенное внесение изменений, позволяющих избежать случайного вывода из строя ранее работающего приложения. В некоторых случаях наиболее подходящим временным решением может стать применение антипаттерна. Несмотря на то что такой шаг может оказаться улучшением по сравнению с исходным кодом, важно отметить, что от этого он не становится паттерном; существуют и другие задокументированные и повторяемые решения, доказавшие свою эффективность. Антипаттерны, рассматриваемые в этой главе, сведены в табл. 5.1. Остальная часть главы описывает каждый антипаттерн, представляя их в порядке важности. Читать главу можно последовательно или изучать только то, что представляет интерес, поскольку разделы не связаны друг с другом. Если читать все подряд не захочется, то советуем обратиться к соответствующим разделам об антипаттернах «Диктатор» и «Локатор сервисов». Наиболее важным считается паттерн внедрения зависимостей через конструктор, а в качестве наиболее часто встречающегося антипаттерна фигурирует «Диктатор». Он сильно мешает применению любой правильной технологии DI, поэтому, прежде чем приступать к изучению других антипаттернов, предлагаем сфокусировать внимание именно на нем. Но наибольшую опасность представляет антипаттерн «Локатор сервисов», поскольку в нем зачастую видят решение проблемы, создаваемой антипаттерном «Диктатор». Он будет рассмотрен в разделе 5.2. 5.1. Антипаттерн «Диктатор» Что противоположно инверсии управления? Изначально понятие «инверсия управления» было придумано для противопоставления обычному положению дел, но мы не можем вести речь об антипаттерне «обычного порядка». Вместо этого для описания класса, не отказывающегося от управления своими нестабильными зависимостями, применим термин «Диктатор». ОПРЕДЕЛЕНИЕ
Антипаттерн «Диктатор» всегда формируется в любом месте, кроме корня композиции, где код полагается на применение нестабильной зависимости. Он нарушает принцип инверсии зависимостей, рассмотренный в подразделе 3.1.2. Антипаттерн «Диктатор» формируется, к примеру, при создании нового экземпляра нестабильной зависимости с помощью ключевого слова new. Реализация антипаттерна «Диктатор» показана в листинге 5.1. При каждом создании нестабильной зависимости получается неявное утверждение о намерении управлять жизненным циклом экземпляра и о том, что никто другой не получит возможности перехватывать данный конкретный объект. Хотя использование ключевого слова new в отношении нестабильной зависимости — пример проблемного кода, к его применению в отношении стабильных зависимостей это не относится. ПРИМЕЧАНИЕ
Не нужно думать, что ключевое слово new вдруг стало «незаконным», но от его использования для получения нестабильной зависимости вне корня композиции нужно воздерживаться. К тому же не следует забывать и о статических классах, которые также могут стать нестабильными зависимостями. Хотя вы никогда не будете использовать ключевое слово new в отношении этих классов, зависимость от этих классов вызывает те же самые проблемы. Наиболее явно антипаттерн «Диктатор» проявляется, когда не предпринимается никаких усилий для ввода в код каких-либо абстракций. Несколько соответствующих примеров вы уже видели в главе 2, когда Мэри создавала свое приложение электронной торговли (см. раздел 2.1). Попытка ввода DI при таком подходе не делается. Но даже там, где разработчики что-то слышали о технологии DI и о возможности создания компоновки, антипаттерн «Диктатор» зачастую в той или иной вариации все же встречается. В следующих подразделах будет показано несколько примеров, напоминающих код, встречавшийся нам на практике. В каждом из рассматриваемых случаев разработчики старались улучшить интерфейсы, но при этом не разбирались как следует в исходных намерениях и мотивах. 5.1.1. Пример: антипаттерн «Диктатор», полученный путем обновления зависимостей с применением ключевого слова new Многие разработчики слышали о принципе программирования для интерфейсов, но не понимали заложенного в него глубокого смысла. Пытаясь все сделать правильно или придерживаться передового опыта, они создавали код, не имеющий особого смысла. Так, в листинге 3.9 был показан вариант ProductService, использующий для извлечения предлагаемых товаров экземпляр интерфейса IProductRepository. Напомним, как выглядел соответствующий код: public IEnumerable<DiscountedProduct> GetFeaturedProducts()
{ return from product in this.repository.GetFeaturedProducts() select product.ApplyDiscountFor(this.userContext); } Здесь важно то, что компонентная переменная repository представляет собой абстракцию. В главе 3 вы видели, как поле repository может быть заполнено внедрением через конструктор, но нам встречались и другие, более примитивные попытки. Одна из них приводится в листинге 5.2. Поле repository объявлено через интерфейс IProductRepository, поэтому любой компонент в классе ProductService (например, GetFeaturedProducts) программируется для интерфейса. Хотя все это представляется правильным, из этого мало что получается, поскольку во время выполнения программы типом всегда будет SqlProductRepository. Способов перехвата или изменения переменной repository нет, если только не изменить и не перекомпилировать сам код. Кроме того, если жестко запрограммировать переменную так, что у нее всегда будет конкретный тип, то из ее объявления абстракцией ничего путного не получится. Непосредственное создание зависимостей с применением ключевого слова new является одним из примеров антипаттерна «Диктатор». Прежде чем перейти к анализу и возможным способам решения возникающих проблем, порождаемых применением антипаттерна «Диктатор», рассмотрим еще несколько примеров для получения более четкого представления о контексте и общих чертах неудачных попыток применения технологии DI. В следующем примере сразу просматривается неудачное решение. Большинство разработчиков попытается усовершенствовать свой подход. Об авторах Стивен ван Дерсен (Steven van Deursen) — фрилансер из Нидерландов, занимающийся разработками и проектированием в среде .NET и получивший богатый опыт работы в этой сфере начиная с 2002 года. Он живет в Неймегене, занимается программированием и зарабатывает этим на жизнь. Помимо программирования Стивен увлекается боевыми искусствами, любит поесть и, конечно же, выпить хорошего виски. Марк Симан (Mark Seemann) — программист, проектировщик программных средств и лектор, живет в столице Дании Копенгагене. Разработкой программных средств он занимается с 1995 года, а технологией TDD увлекается с 2003-го, в том числе шесть лет был консультантом, разработчиком и проектировщиком в компании Microsoft. В настоящий момент Марк занимается разработкой ПО. Он увлекается чтением, рисованием, игрой на гитаре, любит хорошее вино и изысканную еду. Более подробно с книгой можно ознакомиться на сайте издательства » Оглавление » Отрывок Для Хаброжителей скидка 25% по купону — .NET По факту оплаты бумажной версии книги на e-mail высылается электронная книга. =========== Источник: habr.com =========== Похожие новости:
Блог компании Издательский дом «Питер» )[/url], #_.net, #_proektirovanie_i_refaktoring ( Проектирование и рефакторинг ), #_c#, #_professionalnaja_literatura ( Профессиональная литература ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 08:23
Часовой пояс: UTC + 5