Текст книги "Проектирование на UML. Сборник задач"
Автор книги: Антон Хританков
Жанр: Прочая образовательная литература, Наука и Образование
Возрастные ограничения: +12
сообщить о неприемлемом содержимом
Текущая страница: 2 (всего у книги 11 страниц) [доступный отрывок для чтения: 3 страниц]
§2. СЦЕНАРИИ И ВАРИАНТЫ ИСПОЛЬЗОВАНИЯ
ОСНОВНЫЕ ПОНЯТИЯАктором (actor) называется классификатор, который моделирует пользователя или систему, внешнего по отношению к моделируемой системе или компоненту. Акторов, которые используют систему для достижения собственных целей, называют основными. Акторов, которых система использует для достижения целей других акторов, называют второстепенными.
Вариантом использования (use case) называют классификатор, который описывает совокупность сценариев взаимодействия акторов с системой или компонентом для достижения какой-либо цели, значимой для акторов. Варианты использования могут различаться по уровню цели, достижение которой они обеспечивают: высокоуровневые цели, пользовательские цели и отдельные функции системы.
Субъектом (subject) варианта использования называют систему или компонент, взаимодействие акторов с которым он описывает.
Ассоциация (association) актора с вариантом использования указывает на взаимодействие актора с субъектом в одном из сценариев данного варианта использования.
Отношение расширения (extension) между вариантами использования указывает, что при выполнении заданного в точке расширения (extension point) условия сценарий расширяемого варианта использования будет приостановлен, и взаимодействие будет продолжено в рамках расширяющего варианта использования.
Отношение включения (inclusion) указывает, что в процессе выполнения сценарии базового варианта использования вызывают выполнение сценариев включаемого варианта использования.
Как и для других классификаторов, для акторов и вариантов использования определено отношение обобщения (generalization).
ЗАДАЧИ
2.1. Актор User взаимодействует с системой OnlineTranslator в рамках абстрактного варианта использования Translate. Варианты использования TranslateText и TranslateWebPage уточняют Translate. Отобразите на диаграмме вариантов использования.
а. Вариант использования TranslateWebPage включает «include» вариант использования SetURL.
б. Вариант использования SetLanguages расширяет «extend» вариант использования Translate в точке расширения specifyLaguages. Условие расширения «язык не определен автоматически».
в. Добавьте в модель актора ExperiencedUser, уточняющего User. ExperiencedUser может взаимодействовать с системой в рамках варианта использования ProposeTranslation, который уточняет вариант использования TranslateText.
2.2. (см. решение в §11) Автор Author направляет статью SendPaper редактору журнала Editor. Редактор передает статью на рецензирование Review нескольким рецензентам Reviewer. Затем редактор возвращает отзывы рецензентов автору в том же варианте использования SendPaper.
а. Добавьте возможность автору вместе с корректором ProofReader подготовить статью к публикации PrepareForPublishing.
б. Доработайте модель, укажите, что подготовка статьи к публикации выполняется, только если она была одобрена редактором в варианте использования SendPaper.
2.3. Распознавателю текста OCR от модуля морфологии нужны возможность определить, принадлежит ли слово языку, и функция приведения слова к заданной форме, в частности, восстановления начальной формы. Также нужна функция получения грамматического значения конкретного слова.
а. Постройте модель модуля, выделите акторов, варианты использования и укажите отношения между ними.
б. Добавьте функцию вывода слов, похожих на введенное, если его нет в словаре языка. Каким образом данная возможность системы связана с другими функциями?
в. Укажите в модели, что все перечисленные задачи подразумевают выполнение поиска слова (или его основы) в словаре.
г. Некоторые языки могут не поддерживаться системой. Перед выполнением любой функции модуля морфологии нужно проверить, поддержан ли язык. Отобразите это в модели.
2.4. Ответственное лицо ResponsiblePerson может прикрепить документ AttachToIssue к обсуждаемому вопросу, выступая в роли автора author, и к постановлению AttachToResolution, выступая в роли председателя chairman.
а. Покажите в модели, что прикрепление документа выполняется согласно общему сценарию прикрепления, реализуемому в частном случае прикрепления к вопросу или прикрепления к постановлению.
б. Добавьте в модель оператора Operator, который является ответственным лицом с возможностью удаления документов DeleteDocument.
в. Доработайте модель, укажите, что при прикреплении документа рассылается оповещение SendAnnouncement. Несколько операторов могут выступать в роли контролеров controller.
г. Каким образом можно указать, что прикрепление документа возможно только к вопросу или к постановлению? Ответ поясните.
д. (*) Покажите в модели, что ответственное лицо участвует в сценарии прикрепления в роли пользователя user, объединяющей роли автора и председателя. Указание. Используйте производные свойства. См. §4.
2.5. Пользователь User настраивает подключаемые модули аудиоплеера AudioPlayer в рамках варианта использования ConfigurePlugins.
а. Добавьте к варианту использования ConfigurePlugins возможность выбора определенного модуля для настройки SelectPlugin и возможность настройки конкретного модуля ChangeSettings.
б. Добавьте в модель возможность обновить подключаемые модули UpdatePlugins с внешнего сервера PluginsServer.
в. Помимо обычного пользователя в системах обычно есть привилегированный пользователь SuperUser, который имеет права на изменение конфигурации системы. В системе аудиоплеера такой пользователь может обновить модули UpdatePluginsList. Обновление включает в себя удаление DeletePlugin, установку InstallPlugins и просмотр списка доступных на сервере CheckPluginsList.
2.6. Рассмотрим электронную библиотеку научных работ, представленную на рис. 4.
а. Поясните, каким образом используется электронная библиотека. Перечислите актеров и варианты использования.
б. Укажите, что аналитик Analyst принимает участие в индексировании статей, выполняемом в процессе их загрузки бизнес-партнером ContentPartner.
в. Предоставьте возможность исследователю Researcher использовать расширенный поиск AdvancedSearch, который позволяет указать другие параметры поиска в FindPapers.
г. Укажите, что все варианты использования преследуют цели уровня пользователя (user goal) системы. Указание. Уровни целей11
Коберн А. Современные методы описания функциональных требований к системам. – М.:Лори. – 2011. – 288 с.
[Закрыть] не входят в стандарт UML2.
2.7. Клиент Client выполняет операции над своими счетами в банке Bank, используя банкомат ATM в рамках абстрактного варианта использования PerformOperation, который включает информирование об услугах в варианте использования InformAboutServices. Для выполнения операций ATM обращается к платежной системе PaymentSystem.
а. Перечислите основных и вспомогательных акторов системы ATM. Какие из них взаимодействуют с системой в варианте использования PerformOperation?
б. Отразите в модели вариантов использования, что клиенты могут только выполнять операции по получению наличных, в то время как клиенты BankCustomers банка, владеющего банкоматом, могут также оплачивать услуги из списка, предоставляемого банком Bank. При этом сценарии оплаты услуг и получения наличных отличаются между собой, но следуют общему сценарию выполнения операций.
в. Добавьте возможность получения наличных как в валюте счета, так и в другой валюте. При этом в обоих случаях банкомат запрашивает у клиента Client подтверждение на списание средств в валюте счета по курсу банка Bank.
2.8. Во время подготовки данных для морфологического модуля лингвист Linguist взаимодействует с системой подготовки данных MorphoDPS с целью изменения данных ModifyData. Кроме того, для проверки целостности модифицируемых данных лингвисты могут компилировать данные Compile. Компиляция также включает в себя экспорт данных ExportData в формат, понимаемый компилятором. Каждую ночь сервер сборки приложения BuildServer компилирует данные Compile.
а. Добавьте в систему программиста Programmer, которому доступны те же возможности, что и лингвисту. Кроме того, он может экспортировать данные ExportData для отладки подсистемы компиляции данных.
б. Укажите, что для повторного использования словаря, который хранится на сервере данных морфологии, модуль семантики Semantics может взаимодействовать с системой подготовки данных морфологии в варианте использования ExportWordList.
в. Добавьте функции изменения данных: добавление, изменение и удаление слова.
г. Добавьте в модель возможность при изменении данных в некоторых случаях проверять целостность данных перед сохранением в систему.
д. Будет ли проверяться целостность данных при удалении слова? Ответ поясните.
2.9. Инкассатор Cashier и заправщик Loader занимаются обслуживанием автомата с газировкой. В обязанности инкассатора входит сбор денег CollectCash, а заправщик загружает в автомат баллоны с водой ChangeWater и газом ChangeGas.
а. Выделите в модели общий сценарий обслуживания, который включает авторизацию в системе обслуживания автомата и завершение сессии обслуживания.
б. Укажите, что автомат также может быть заправлен сиропом.
в. В каком случае инкассатор может загрузить в автомат баллон с водой? Ответ поясните.
г. Отразите в модели, что инкассатор может наблюдать за автоматом через Интернет с помощью встроенной видеокамеры с включением по сигналу датчика присутствия здания. Решение поясните.
2.10. Первоначальная модель загрузки CreateDocument и проверки документов ReviewDocument преподавателем Professor учебного заведения приведена на рис. 5.
а. Добавьте преподавателю возможность создавать курсы, как по шаблону, так и повторяя курс прошлого года.
б. Покажите на диаграмме, что у преподавателя есть три возможности проверки документа: с помощью мастера GuidedReview, совместно со студентом JointReview, и простое ревью BasicReview. При этом студенты сами могут загружать документы Upload и регистрироваться Enroll на курс.
в. Добавьте в модель ассистента преподавателя TA так, чтобы он обладал всеми обозначенными выше возможностями преподавателя, но не мог создавать курсы. При этом студент может быть ассистентом, но не преподавателем.
§3. КООПЕРАЦИИ И ВЗАИМОДЕЙСТВИЯ КЛАССОВ
ОСНОВНЫЕ ПОНЯТИЯСтруктурированным классификатором (structured classifier) называется классификатор, который может включать соединители, связывающие содержащиеся в классификаторе свойства. Структурированные классификаторы определяют контекст для соединителей и позволяют описать связи между экземплярами, возникающие во время выполнения системы.
Соединители (connector) – это черты структурированного классификатора, имеющие тип и связывающие два или более свойств классификатора. В то время как связи (links) являются экземплярами ассоциаций, соединители ограничивают возможные связи между экземплярами в зависимости от контекстного классификатора, которому принадлежат эти экземпляры.
Частью (part) классификатора называется свойство, с которым классификатор связан отношением композиции.
Кооперация (collaboration) является структурированным классификатором, обладающим поведением, и определяет роли составляющих ее частей и взаимодействия между ними в контексте кооперации. Кооперации используются для определения взаимодействий, обеспечивающих достижение какой-либо цели или реализации функции системы.
Вхождение кооперации (collaboration use) связывает элементы модели с ролями, определенными в кооперации.
Ролями (role) в кооперации называют части кооперации, параметры поведения или локальные переменные в поведении кооперации.
Состоянием (state) экземпляра классификатора называют условие или ситуацию, во время которой его свойства удовлетворяют некоторому условию, он выполняет определенное собственное поведение или ожидает какого-либо события.
Поведение (behavior) классификатора описывает изменение состояния классификатора с течением времени в ответ на внешние события и внутренние вычисления. Поведение может быть исполняемым (executable) и производным (emergent). Исполняемое поведение является описанием процесса исполнения некоторого алгоритма экземпляром классификатора путем выполнения действий. Производное поведение возникает в результате взаимодействия нескольких экземпляров.
Суть овеществления (reification) заключается в представлении происходящего поведения в виде экземпляра класса поведения. Таким образом, выполнение поведения экземплярами классов отождествляется с созданием и уничтожением экземпляра класса этого поведения.
Событием (event) называется описание группы изменений, которые могут привести к модификации значений свойств экземпляров в модели или выполнению поведения.
Сигнал (signal) является специальным видом классификатора, который описывает асинхронные запросы, направляемые экземплярам классификаторов. Черта приема определенного типа сигнала (reception) указывает, что экземпляры принимающего активного класса обрабатывают направленные им сигналы данного типа.
Траекторией (trace) называется частично упорядоченная последовательность возникновений событий (occurrence specification).
Взаимодействием (interaction) называется производное поведение участников, указывающее разрешенные и запрещенные траектории. Участникам сопоставлены линии жизни (lifeline), на которых откладываются возникающие на траекториях события. Когда взаимодействие происходит в контексте динамического структурированного классификатора, линии жизни соответствуют ролям в этом классификаторе, локальным переменным данного взаимодействия или параметрам вызываемых операций и отправляемых сигналов. Если кратность участвующего во взаимодействии свойства, переменной или параметра больше единицы, то для соотнесения линии жизни с определенным значением из нескольких используются селекторы (selector).
Сообщения (message), передаваемые в процессе взаимодействия, могут быть нескольких сортов: синхронный и асинхронный вызов операции, асинхронная отправка сигнала, создание и уничтожение экземпляра, и ответные (reply) сообщения. Передача сообщения между линиями жизни отмечается возникновением событий отправки и получения сообщения. Если отправитель или получатель находится вне взаимодействия, вместо него подставляется шлюз (gate).
Фрагмент взаимодействия (interaction fragment) является частью взаимодействия и включает множества разрешенных и запрещенных подпоследовательностей возникновений событий для всех или некоторых линий жизни.
Операторы взаимодействия (interaction operator) используются для изменения траекторий комбинированного фрагмента взаимодействия, состоящего из нескольких фрагментов. Определены операторы альтернативного выбора (alt), цикла (loop), параллельного возникновения событий фрагментов (par), условного выполнения (opt) и другие.
Спецификация исполнения (execution specification), отложенная на линии жизни, указывает на выполнение экземпляром классификатора соответствующего данной линии некоторого исполняемого поведения.
Вхождение взаимодействия (interaction use) служит для повторного использования взаимодействий, вместо фрагмента подставляется содержимое указанного взаимодействия.
ЗАДАЧИ
3.1. (см. решение в §11) Кооперация продажа Sale включает роли продавец Salesman и покупатель Customer.
а. Покажите, что продавец и покупатель могут взаимодействовать друг с другом.
б. Используя вхождения коопераций Sale, создайте модель кооперации продажи с посредником BrokeredSale, в которой покупатель взаимодействует с посредником Broker, а посредник как покупатель взаимодействует с продавцом.
3.2. Моделируется серверная часть веб-приложения интернет-магазина, построенного на основе паттерна Model-View-Controller (MVC). Взаимодействие между ролями Model, View и Controller отобразим на диаграмме последовательности.
а. Разместите на диаграмме роли Controller, Model, а также роль типа ORM с именем db. Синхронное найденное сообщение postBuy (purchase) приходит на линию жизни Controller. После этого Controller посылает Model синхронное сообщение addPurchase (purchase). В ответном сообщении Model возвращает объект purchaseDetails.
б. Реализуем на диаграмме поведение Model в ответ на сообщение addPurchase. Model посылает синхронное сообщение addPurchase (purchase) линии жизни db. Затем открывается фрагмент alt. При условии purchase.needDelivery Model посылает сообщение addDelivery(purchase.address) линии жизни db. Фрагмент alt окончен. Model посылает db синхронное сообщение saveChanges ().
в. В ответ на запрос покупки Controller должен сообщить пользователю, что заказ совершен успешно. После получения от Model ответного сообщения Controller создает новую линию жизни с ролью View сообщением createConfirmationView. Затем Controller посылает линии жизни View сообщение setPurchaseDetails (purchaseDetails). Затем Controller отправляет в ответ на входящий запрос ответное сообщение, содержащее View.
г. Отдел доставки нужно уведомить о том, что требуется доставить новый заказ. Добавим в Model (в опциональный фрагмент alt) посылку асинхронного сообщения notifyNewDelivery. Сообщение является потерянным.
д. Код интернет-магазина достаточно универсальный. Можно сделать на базе этого кода несколько сайтов для разных магазинов. Для этого нужно заполнить View при создании информацией о конкретном магазине. Добавим в контекст взаимодействия переменную shopInfo типа ShopInformation. После сообщения setPurchaseDetails начинается фрагмент использования взаимодействия ref с именем FillShopInformation. В этот фрагмент входят линии жизни Controller и View. В виде аргумента во взаимодействие передается переменная shopInfo.
3.3. (см. решение в §1) Автор Author направляет статью сообщением manuscript редактору Editor и ожидает от него подтверждения получения. Редактор отправляет сообщением evaluate статью рецензенту Peer. Рецензент отправляет сообщение редактору с оценкой статьи review. Редактор направляет сообщение автору с результатами resolution и рецензенту с благодарностью thanks.
а. Восстановите структурную модель взаимодействия в виде кооперации ReviewManuscript, укажите кратность роли рецензента так, чтобы статья направлялась на рецензию одному из пяти рецензентов.
б. Укажите, используя фрагменты, что статья направляется на рецензирование каким-либо трем из пяти рецензентов.
в. Используя фрагменты, покажите, что порядок отправки результатов рецензирования автору и благодарностей рецензентам не имеет значения.
3.4. Терапевт Therapist ведет прием посетителей Person, в ходе которого выписывает лекарства Medicine; посетители принимают лекарства.
а. Постройте логическую модель, включающую классы Therapist, Medicine и Person, и отношения между ними.
б. Используя кооперации, покажите, что на приеме терапевт выполняет обязанности врача Doctor, посетитель является пациентом Patient, лекарства выписываются в виде рецептов Subscription.
3.5. Автомобиль Car состоит из двигателя класса Engine, пары передних front и задних rear колес класса Wheel.
а. Добавьте привод drivetrain так, чтобы автомобиль был переднеприводным.
б. Расширьте модель так, чтобы наряду с переднеприводными автомобилями, она описывала полноприводные автомобили как частный случай переднеприводных. Добавьте необходимые элементы, используйте двигатель DoubleEngine с двумя приводами типа drivetrain.
3.6. Пассажир Person заходит в лифт и нажимает кнопку pressButton лифта Lift с указанием целочисленного номера этажа floor. Лифт закрывает двери и начинает движение синхронным вызовом операции startMoving. После этого сообщает пассажиру номера проезжаемых лифтом этажей сообщением floorReached с указанием номера этажа. Затем лифт вызывает операцию stopMoving и останавливается. Пассажир нажимает кнопку pressDoors лифта для открытия дверей.
а. Как можно уточнить модель взаимодействия, если известно, что лифт обслуживает с первого по пятый этажи?
б. Уточните взаимодействие пассажира с лифтом. Укажите, что до нажатия кнопки этажа, пассажир обязан закрыть двери кнопкой pressDoors.
в. Используя фрагменты, покажите, что пассажир не может нажать кнопку открытия и закрытия дверей в процессе движения лифта.
г. (*) Укажите, что лифт проезжает один этаж за три секунды.
3.7. Менеджер подключаемых модулей pluginsManager класса PluginsManager получает сообщение loadPlugins – указание на необходимость загрузки доступных модулей. Он синхронно запрашивает у объекта settings класса PluginManagerSettings пути к директориям с модулями и получает от settings значение свойства pluginsDirs. После чего в цикле для каждой директории и каждой библиотеки *.dll загружает модули вызовом собственной операции loadPlugins, передавая путь к библиотеке в параметрах.
а. Реализуйте операцию loadPlugins класса PluginsManager. Взаимодействие начинается с создания нового экземпляра PluginsDll, затем идет получение количества подключаемых модулей в библиотеке getPluginsCount и получение всех модулей через вызовы getPlugin с параметром – номером модуля. После этого происходит инициализация каждого полученного модуля IPlugin вызовом метода initPlugin класса PluginsManager.
б. Добавьте в модель описание действий по инициализации модуля. Метод initPlugin проверяет, обрабатывает ли модуль события графического интерфейса вызовом isUIHandled. Если обрабатывает, то регистрирует модуль в качестве слушателя событий addListener в классе PlayerUIPresenter.
3.8. Взаимодействие выбора этажа SelectFloor содержит линию жизни кабины, представленной экземпляром активного класса Cabin, линию жизни floor экземпляра класса кнопки этажа FloorButton с селектором «1», и линию жизни класса Algorithm. Взаимодействие начинается с синхронного вызова кабиной операции нажатия кнопки isPressed на линии floor. Операция возвращает логическое значение «истина», если кнопка нажата. Затем экземпляр класса Cabin вызывает операцию selectFloor у алгоритма на линии в данном взаимодействии.
а. Используя оператор цикла, покажите, что проверяется нажатие кнопок всех этажей. Переменную цикла, содержащую номер кнопки этажа, объявите как атрибут взаимодействия.
б. Уточните взаимодействие, добавив вызовы операции указания алгоритму этажей, кнопки которых нажаты.
в. Пусть взаимодействие описывает поведение кооперации, линии жизни соответствуют ролям в этой кооперации. Каким образом необходимо изменить взаимодействие, если кооперация владеет ролью с линией жизни алгоритма?
г. (*) Приведите по одному примеру разрешенной и неразрешенной траекторий в данном взаимодействии.
3.9. Пользовательский интерфейс мультимедиа проигрывателя PlayerView сообщает ev контроллеру UIPresenter о начале проигрывания элемента списка воспроизведения playEntry. Контроллер: 1) рассылает это сообщение всем своим слушателям собственной операцией raiseEvent; 2) получает от управляющего компонента Engine тип контента по имени name выбранного элемента item события ev вызовом getContentType; 3) по типу контента получает медиапоток вызовами getAudioStream или getVideoStream; 4) переводит интерфейс в состояние проигрывания либо видео, либо аудио вызовом setPlaybackMode и указывает медиапоток операцией setMediaStream; 5) запускает воспроизведение операцией play контроллера.
а. Добавьте в модель возможность параллельной обработки сообщений в raiseEvent так, что операция обработки handleEvent вызывается асинхронно у каждого зарегистрированного слушателя типа IUIListener. Для отображения в модели используйте два анонимных слушателя.
б. Уточните взаимодействие при обработке события запуска проигрывания песни модулем lyricsPlugin, зарегистрированным в качестве слушателя событий UIPresenter. Модуль определяет тип контента элемента, передаваемый в сообщении о запуске. Для аудиоконтента создает запрос о тексте песни, вызывая собственный метод makeRequest. Затем модуль асинхронно вызывает операцию dispatchRequest класса NetController, передавая в параметрах запрос и себя как обработчика ответа. Получив ответ от сервера, NetController передает processResponse его обработчику. Модуль вызывает displayPage у UIPresenter и передает полученную HTML-страницу с текстом песни
3.10. (*) При решении основных задач, морфологический модуль ищет запрашиваемое слово в словаре языка. Таким образом, многие задачи в модуле зависят от реализации функции поиска слова в словаре. См. диаграмму на рис. 8. Для уменьшения объема, который занимает словарь, используется тип данных префиксное дерево22
Префиксное дерево, «Trie», http://en.wikipedia.org/wiki/Trie.
[Закрыть]. Каждый узел дерева содержит ассоциативный массив буква – следующий узел. Таким образом, если слово есть в словаре, то в дереве есть путь, начинающийся в корне и проходящий по вершинам, соответствующим буквам слова.
а. Постройте модель кооперации поиска слова в словаре, используя роли класса Dictionary и узла дерева Node. Для моделирования префиксного дерева используйте квалифицированные соединители и булевый атрибут leaf, указывающий на конечный узел слова.
б. Добавьте в модель поведение кооперации. Актор парсер строки StringParser вызывает операцию hasWord класса Dictionary с параметром word типа String. Метод hasWord, реализующий операцию hasWord, получает корневой узел с помощью операции getRoot класса Dictionary. Получив экземпляр класса Node, метод в цикле для каждой следующей буквы слова word вызывает у этого экземпляра операцию getNextLetter с параметром c типа char – текущей буквы слова. Данная операция возвращает дочерний узел дерева. Когда буквы слова word закончились, нужно вернуть актору значение операции isLeaf последнего полученного узла Node.
в. Модифицируйте поведение. Если в какой-то момент вызов getNextLetter прерван по исключению NoSuchLetter, операция hasWord должна вернуть false.
3.11. Вариант использования просмотр каталога SearchCatalog реализован кооперацией GetAllRecords. Основной сценарий варианта использования начинается с получения контроллером приложения AC команды showRecords. AC отображает show в пользовательском интерфейсе UI сообщение «Идет запрос». AC параллельно отправляет источнику данных DataSource запрос readRecords. DataSource передает AC одну запись в параметре действия acceptRecord. Затем AC показывает запись в UI.
а. Укажите, что перед запросом записи, AC запрашивает getListSize количество записей у источника данных. Результат присваивается переменной listSize.
б. Измените модель так, чтобы источник данных передавал контроллеру listSize записей по одной, а контроллер отображал show в UI все полученные записи вместе.
в. Реализуйте альтернативный сценарий, когда источник данных не содержит записей.
г. Перечислите все необходимые соединители в кооперации GetAllRecords, укажите, какие сообщения по ним передаются. Ответ поясните.
д. (*) Какое минимальное количество экземпляров классов необходимо, чтобы выполнить описанное поведение? Ответ поясните.
Правообладателям!
Данное произведение размещено по согласованию с ООО "ЛитРес" (20% исходного текста). Если размещение книги нарушает чьи-либо права, то сообщите об этом.Читателям!
Оплатили, но не знаете что делать дальше?