Принцип инверсии зависимости

12 декабря 2009 г.

Формулировка:

  • Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции.
  • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Сейчас мы создадим и отрефакторим приложение. Будем двигаться по шагам и я покажу применение принципа инверсии зависимостей в действии.

Шаг 1. Сильная связанность

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

public class Program 
{
    public static void Main()
    {
        var reporter = new Reporter();
        reporter.SendReports();
    } 
}

Главный объект в нашей бизнес-логике – Reporter.

public class Reporter 
{
     public void SendReports()
     {
        var reportBuilder = new ReportBuilder();
        IList<Report> reports = reportBuilder.CreateReports();
 
        if (reports.Count == 0)
            throw new NoReportsException();
 
        var reportSender = new EmailReportSender();
        foreach (Report report in reports)
        {
            reportSender.Send(report);
        }
    }
}

Устроен Reporter очень просто. Он просит ReportBuilder создать список отчетов, а потом один за другим отсылает их с помощью объекта EmailReportSender.

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

Тестируемость

Как протестировать функцию SendReports? Давайте проверим поведение функции, когда ReportBuilder не создал ни одного отчета. В этом случае она должна создать исключение NoReportsException:

public class ReporterTests 
{
     [Fact]
     public void IfNotReportsThenThrowException()
     {
         var reporter = new Reporter();
         reporter.SendReports();
         // ???
     }
}

Как в этом случае задать поведение объектов, которые использует Reporter? Мы же должны «сказать» ReportBuilder'у вернуть пустой список, и тогда функция SendReports выбросит исключение. Но в текущей реализации Reporter'а сделать мы этого не можем. Получается, мы не можем задать такие входные данные, при которых SendReports выкинет исключение. Значит в данной реализации объект Reporter очень плохо поддается тестированию.

Связанность

Дело в том, что функция SendReports, кроме своей прямой обязанности, слишком много знает и умеет:

  • знает, что именно ReportBuilder будет создавать отчеты
  • знает, что все отчеты надо отсылать через email с помощью EmailReportSender
  • умеет создавать объект ReportBuilder
  • умеет создавать объект EmailReportSender

Здесь нарушается принцип единственности ответственности. Проблема заключается в том, что в данный момент внутри функции SendReports объект ReportBuilder создается оператором new. А если у него появятся обязательные параметры в конструкторе? Нам придется менять код в классе Reporter да и во всех других классах, которые использовали оператор new для ReportBuilder'а.

К тому же, первые пункты нарушают принцип открытости/закрытости. Дело в том, что если мы захотим с помощью нашей утилиты отсылать сообщения через SMS, то придется изменять код класса Reporter. Вместо EmailReportSender мы должны будем написать SmsReportSender. Еще сложнее ситуация, когда одна часть пользователей класса Reporter захочет отправлять сообщения через emal, а вторая через SMS.

Обратите внимание, что наш объект Reporter зависит не от абстракций, а от конкретных объектов ReportBuilder и EmailReportSender. Можно сказать, что он "сцеплен" с этими классами. Это и объясняет его хрупкость при изменениях в системе. Может оказаться, что Reporter жестко зависит от двух классов, эти два класса зависят еще от 4х других. Получится, что вся система – это клубок из стальных ниток, который нельзя ни изменить, ни протестировать. Этот пример наглядно показывает нарушение принципа инверсии зависимостей.

Шаг 2. Применяем принцип инверсии зависимостей

Сейчас несколькими простыми действиями мы решим наши проблемы с Reporter'ом.

Для начала вынесем интерфейсы IReportSender из EmailReportSender и IReportBuilder из ReportBuilder.

public interface IReportBuilder 
{
    IList<Report> CreateReports();
}
 
public interface IReportSender 
{
    void Send(Report report);
}

Теперь вместо того, чтобы создавать объекты в функции SendReports, мы передами их объекту Reporter в конструктор:

public class Reporter : IReporter 
{
     private readonly IReportBuilder reportBuilder;
     private readonly IReportSender reportSender;
 
     public Reporter(IReportBuilder reportBuilder, IReportSender reportSender)
     {
         this.reportBuilder = reportBuilder;
         this.reportSender = reportSender;
     }
 
     public void SendReports()
     {
        IList<Report> reports = reportBuilder.CreateReports();
 
        if (reports.Count == 0)
            throw new NoReportsException();
 
        foreach (Report report in reports)
        {
            reportSender.Send(report);
        }
    }
}

Во время создания объекта Reporter в самом начале программы мы будем задавать конкретные IReportBuilder и IReportSender и передавать их в конструктор:

public static void Main()
{
     var builder = new ReportBuilder();
     var sender = new SmsReportSender();
     var reporter = new Reporter(builder, sender);
 
     reporter.SendReports();
}

Посмотрим, какие проблемы мы смогли решить.

Тестируемость

Теперь у нас есть возможность передавать в конструктор Reporter'а объекты, которые реализуют нужные интерфейсы. Давайте подставим mock-объекты и зададим нужное нам поведение:

public class ReporterTests 
{
     [Fact]
     public void IfNotReportsThenThrowException()
     {
        var builder = new Mock<IReportBuilder>();
        builder.Setup(m => m.CreateReports()).Returns(new List<Report>());
 
        var sender = new Mock<IReportSender>();
 
        var reporter = new Reporter(builder.Object, sender.Object);
 
        Assert.Throws<NoReportsException>(() => reporter.SendReports());
    }
}

Тест прошел! Мы отлично справились. Теперь есть возможность задавать поведение объектов, с которыми работает наш Reporter. И в данном случае нам не важно, что где-то есть EmailReportSender, SmsReportSender или еще какой-то *ReportSender. Тесты Reporter'а не зависят от других реализаций, мы используем только интерфейсы. Это делает тесты более устойчивыми к изменениям в системе.

Связанность

Мы реализовали на практике главный принцип инверсии зависимостей. Наш Reporter зависит только от абстракций (интерфейсов).

Как быть, если мы хотим отсылать отчеты не через email, а через SMS? Теперь сделать это проще простого . Надо передать в конструктор Reporter'а не EmailReportSender, а SmsReportSender. Код самого Reporter'а мы изменять уже не будем.

Тем не менее меня такое решение еще не полностью устраивает. Мне не нравится, что где-то в клиентах моей библиотеки будет куча строк типа:

var builder = new ReportBuilder(); 
var sender = new SmsReportSender(); 
var reporter = new Reporter(builder, sender); 
 
// ...  

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

Шаг 3. Используем ServiceLocator

Наша цель — задавать соответствие интерфейсов и их реализаций. Сделаем наше приложение конфигурируемым на клиенте!

Нам нужен объект, который будет хранить информацию о том, что интерфейсу IReportSender соответствует реализация EmailReportSender. Назовем этот объект ServiceLocator. Связь интерфейса и реализации он будет хранить во внутреннем словаре:

public static class ServiceLocator 
{ 
    private static readonly Dictionary<Type, Type> services = new Dictionary<Type, Type>();
 
    public static void RegisterService<T>(Type service)
    {
        services[typeof (T)] = service;
    }
 
    public static T Resolve<T>()
    {
        return (T) Activator.CreateInstance(services[typeof (T)]);
    }
}

Теперь рассмотрим, как мы будем его использовать. Для начала зарегистрируем связи:

public static void Main() 
{
     ServiceLocator.RegisterService<IReportBuilder>(typeof(ReportBuilder))
     ServiceLocator.RegisterService<IReportSender>(typeof(SmsReportSender));

Теперь у класса Reporter создадим конструктор, который пользуется этими настройками:

public class Reporter : IReporter 
{
    private readonly IReportBuilder reportBuilder;
    private readonly IReportSender reportSender;
 
    public Reporter() : this(ServiceLocator.Resolve<IReportBuilder>(), ServiceLocator.Resolve<IReportSender>())
    {
    }
 
    public Reporter(IReportBuilder reportBuilder, IReportSender reportSender)
    {
        this.reportBuilder = reportBuilder;
        this.reportSender = reportSender;
    // ...

Примечание: второй конструктор отлично подойдет для модульного тестирования.

После инициализации ServiceLocator'а вызываем в любом месте программы пустой конструктор:

var reporter = new Reporter(); 
reporter.SendReports();  

С таким подходом мы можем задать соответствие интерфейсов и их реализаций один раз и использовать его. Чтобы во всем приложении вместо SmsReportSender использовать EmailReportSender, надо в начале выполнения программы (сайта, сервиса и т.д.) изменить:

ServiceLocator.RegisterService<IReportSender>(typeof(SmsReportSender));

на другую реализацию IReportSender'а:

ServiceLocator.RegisterService<IReportSender>(typeof(EmailReportSender));

В чем же отличие? Дело в том, что раньше классы сами знали от каких объектов они зависят и могли напрямую использовать друг друга:

Теперь объекты знают только про интерфейсы классов, с которыми взаимодействуют, а реализации просят у сервиса:

Используем готовый IoC (inversion of control) контейнер

Конечно, решение получилось достаточно гибким, но сама реализация класса ServiceLocator еще требует доработки. Например, что делать, если реализацию интерфейса IReportBuilder напротяжении жизни приложения нужно создавать только один раз, а потом при обращении к функции Resolve возвращать созданную реализацию? Нашему ServiceLocator'у не хватает настроек поведения. К счастью, изобретать свой велосипед не надо. На данный момент существуют довольно много готовых IoC контейнеров. Вот самые популярные:

Вкратце, покажу преимущества от их использования на примере Ninject.

Для начала отчистим класс Reporter от лишнего конструктора, оставим только конструктор с параметрами:

public class Reporter : IReporter
{
    private readonly IReportBuilder reportBuilder;
    private readonly IReportSender reportSender;
 
    public Reporter(IReportBuilder reportBuilder, IReportSender reportSender)
    {
        this.reportBuilder = reportBuilder;
        this.reportSender = reportSender;
    }
 
    // ...

Теперь вначале исполнения программы инициализируем контейнер и вызываем отправку отчетов:

public class Program
{
    public static void Main()
    {
        IKernel kernel = new StandardKernel(new InlineModule(
                            m => m.Bind<IReportBuilder>().To<ReportBuilder>(),
                            m => m.Bind<IReportSender>().To<EmailReportSender>(),
                            m => m.Bind<Reporter>().ToSelf()
                            ));
 
        var reporter = kernel.Get<Reporter>();
 
        reporter.SendReports();
    }
}

При создании экземпляра Reporter Ninject с помощью метода Get сам подставит в конструктор объекта реализации IReportBuilder и IReportSender. Это инжекция в конструктор. Есть и другие способы инжектирования зависимостей. Я советую использовать готовые IoC контейнеры в своих проектах.

Инвертированная архитектура

Итак, рассмотрев детали мы можем подняться выше. Взглянем на архитектуру приложения. Давайте рассмотрим классическую трехзвенную архитектуру:

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

1. Жесткость

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

2. Хрупкость

Когда вы вносите изменения в одну часть системы, то в неожиданном месте ломается другая.

3. Неподвижность

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

"Выберите любой класс на бизнес-уровне, допустим InvoiceService, и просто скопируйте его код в новый проект. Попробуйте его скомпилировать. Скорее всего выяснится, что не хватает каких-то зависимостей: Invoice, InvoiceValidator и т. д. Скопируйте и эти классы в проект и повторите попытку. Скорее всего и на этот раз каких-то классов система недосчитается. И когда, в конечном итоге, вам все же удастся скомпилировать приложение, вы обнаружите, что в новом проекте находится добрая доля исходного кода."

К чему мы пришли:

В данном случае каждый слой отдельно представлен абстрактными классами/интерфейсами. Сам слой наследуется от этого абстрактного слоя (например, Business Layer реализует интерфейсы, которые объявлены в Business Layer Abstract). Все классы верхнего уровня используют нижележащий уровень через его абстрактный слой. Таким образом ни один слой не зависит от деталей другого. Напротив, они зависят только от абстракций.

Тут есть вопрос по реализации. Как класс из UI Layer узнает во время исполнения программы, какую реализацию IReportSender'а надо использовать? Ведь у него нет доступа к слою Business Layer. Ответ уже был дан выше – мы запишем все зависимости в IoC конейнер. Потом вызываем container.Get(). А там уже по цепочке через инжекцию (например, в конструктор) создадутся все необходимые объекты.

Исходный код на Github

Этот пост входит в серию Принципы проектирования классов (S.O.L.I.D.):


Ссылки

Роберт Мартин: The Dependency Inversion Principle

dnrTV: James Kovacs' roll-your-own IoC container

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

Inversion of Control Containers and the Dependency Injection pattern

Использование инверсии управления (IoC) в сигнатурах методов

Инверсия зависимостей при проектировании Объектно-Ориентированных систем

NDC 2009, Ayende Rahien - Inversion of Control & Dependency Injection Breaking out from the Dependency Hell

Wikipedia: Dependency injection

46 комментариев:

  1. Отличный пост!
    Только исправь во второй строке кода после фразы: "Чтобы во всем приложении вместо SmsReportSender использовать EmailReportSender, надо в начале выполнения программы (сайта, сервиса и т.д.) изменить..." c
    (typeof(SmsReportSender));
    на
    (typeof(EmailReportSender));

    ОтветитьУдалить
  2. Соглашусь. Пост отличный, хорошо все расписано.

    ОтветитьУдалить
  3. А можно вопрос ;) незнаю даже как сформулировать ;)

    Вот везде в DDD советуют выносить получение entity в отдельный репозиторий, ну тоесть писать

    чтото вроде:
    Product product = productRepository.getbyid(id);

    а не вот так:
    Product product = new Product(id)
    product.load();

    Но такое движение как-то мне не нравится. Это конечно хорошо, что DB код вынесен в репозиторий,
    а в самой entity осталась только бизнесс логика. Но допустим у нас есть класс Order, который этот Product и использовал,
    и вот теперь ему придется знать не только про товар, но еще и про то, что надо использовать репозиторий этих товаров.
    Была одна зависимость - стало их две.

    Есть идея дублировать методы в таком духе, а репозитории вообще делать internal

    class Product
    {
    static Product getbyid(int id)
    {
    //productRepository заинжектили
    return productRepository.getbyid(id);
    }
    static Product getByBarcode(string barcode)
    {

    return productRepository.getbybarcode(barcode);
    }
    static Boolean save()
    {
    return productRepository.save(this);
    }

    }

    Ну и использовать соотвественно вот так

    Product product = Product.getbyid(id);
    product.price=1;
    product.save();

    Как думаешь это плохой подход? какие могут проблемы потом с таким кодом?

    ОтветитьУдалить
  4. @betspaam

    Привет!
    Я уже писал, что думаю по поводу ActiveRecord

    http://blog.byndyu.ru/2009/10/blog-post.html

    Ты писал
    > Но допустим у нас есть класс Order, который этот Product и использовал
    > Была одна зависимость - стало их две

    Приведи пример в коде, иначе не понятно о чем ты говоришь. Можешь мне на мыло написать.

    ОтветитьУдалить
  5. Александр, у меня вопрос. Можно считать, что ServiceLocator является неотъемлемой частью паттерна инверсии зависимости, или это все же полезное дополнение, усиливающее эффект паттерна? Если мы попробуем отказаться от ServiceLocator и вернуться к соотв. фрагменту кода:

    1: var builder = new ReportBuilder();
    2: var sender = new SmsReportSender();
    3: var reporter = new Reporter(builder, sender);

    а затем представить, что создание объекта Reporter происходит не в ф-ии Main, а в методе какого-то класса X нашей бизнес-логики, тогда будет ли какая-то польза от такой инверсии? Да, мы ослабим зависимости класса Reporter, заменив конкретные типы контрактами интерфейсов, но эти зависимоти теперь попадают в метод класса X. Выходит, мы не избавляемся от проблемы, а попросту перекладываем ее на другой объект, и без локатора зависимости так и буду блуждать в пределах модуля?

    ОтветитьУдалить
  6. @Sergey
    Отличный вопрос!

    Начну по порядку.

    > неотъемлемой частью паттерна инверсии зависимости...
    Поправлю, что это не шаблон проектирования, а принцип. Шаблоны заточены под решение более конкретных задач. Их особенностью является то, что при использовании шаблонов мы не нарушаем ни один принцип проектирования.

    > Можно считать, что ServiceLocator является неотъемлемой частью паттерна инверсии зависимости...
    ServiceLocator это шаблон проектирования. Т.е. это один из способов добиться нужного результата. Это такой же инструмент, как и IoC контейнеры.

    > создание объекта Reporter происходит не в ф-ии Main, а в методе какого-то класса X нашей бизнес-логики, тогда будет ли какая-то польза от такой инверсии?
    Определенно будет. Если ваши объекты будут зависеть от интерфейсов, то архитектура вашего приложения от этого только выиграет.

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

    1: var builder = new ReportBuilder();
    2: var sender = new SmsReportSender();
    3: var reporter = new Reporter(builder, sender);

    можно спрятать в фабрике класса Reporter и тем самым скрыть зависимость от реализаций в одном месте.

    ОтветитьУдалить
  7. Тогда мы локатор заменяем фабрикой. Т.е. в любом мы должны сочетать IoC либо с локатором либо с фарикой, чтобы сделать все части модуля не связанными с конкретными типами? А что по поводу IoC в чистом виде?

    По семантики слов "паттерн/шаблон" и "принцип": я правильно понимаю, что IoC вы считаете "принципом", а локатор и IoC контейнеры -- "паттернами"? В словаре у слова pattern очень широкий набор значений (образец, модель, пример, шаблон, принцип, система, структура, характер, стиль и т.д.), поэтому тут есть шанс основательно запутаться.

    ОтветитьУдалить
  8. Сделаю попытку дать свой вариант ответа на свой же вопрос. Поскольку я мало что читал и применял, не исключено, что мое заключение может быть ошибочным:
    IoC без сочетания с фабриками/локаторами позволит ослабить связи внутри модуля только до определенной степени. Фактически этого же можно добиться, если просто заменить явные типы их интерфейсами везде, где только можно.

    ОтветитьУдалить
  9. @Sergey
    Для начала советую посмотреть/почитать ссылки, которые даны в конце статьи.

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

    ОтветитьУдалить
  10. Код выделяется вместе с номерами строк.
    Плохо.

    ОтветитьУдалить
  11. И что используется для тестирования? атрибуты намекают на то, что это не MsTests.

    ОтветитьУдалить
  12. @reviews

    "И что используется для тестирования?"

    xUnit

    ОтветитьУдалить
  13. Считается, что глобальное состояние, от которого все зависят — это плохо. Но ведь рассуждения, приводящие к такому выводу, можно применить и к IoC-контейнеру? В частности, ваш ServiceLocator — чем не глобальная переменная?

    ОтветитьУдалить
  14. @plain_fact
    Все правильно. ServiceLocator - это глобальная статическая переменная, поэтому я его и заменил за IoC-контейнер.

    ServiceLocator можно считать одним из этапов развития инжектирования зависимостей. Он являет собой пассивное внесение зависимостей.

    ОтветитьУдалить
  15. Александр, вам блог у меня уже был в избранном, но только сейчас нашел время начать его читать. И это было первая статья.

    Спасибо!! Отличный пост. Все идеально расписано. Буду изучать дальше :)

    ОтветитьУдалить
  16. @Maxim
    Рад, что полезно :)

    Теперь мы можем решать проблемы с кодом вместе, присоединяйтесь http://blog.byndyu.ru/2010/05/blog-post.html

    ОтветитьУдалить
  17. Интересная статья, спасибо. Я тоже изложил свое видение IoC: http://dotsid.blogspot.com/2009/12/ioc.html

    ОтветитьУдалить
  18. Небольшое замечание.
    После рефакторинга первоначальной версии программы класс Reporter унаследован от интерфейса IReporter, который нигде не описан.

    ОтветитьУдалить
  19. ServiceLocator надругается над принципом KISS. Почему недостаточно обойтись так:

    class Global {
    public static final IReportBuilder reportBuilder;
    public static final IReportSenderr reportSender;
    static {
    reportBuilder = new ReportBuilder();
    reportSender = new SmtpReportSender();
    }
    // это const-вариант, можно и с getter/setter
    ...
    }


    Какое преимущество, перевешивающие KISS, дают IoC контейнеры?

    ОтветитьУдалить
  20. @sham-bho

    Это на каком языке программирования?

    Что значит static {} посреди класса?

    ОтветитьУдалить
  21. @Александр Бындю
    на сколько я помню это блок статической инициализации в c#

    ОтветитьУдалить
  22. @Nuts

    Я думаю, что это что-то типа Java, судя по слову final.

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

    Плюс теряется возможность настройки жизненного цикла объекта, что позволяют почти все IoC.

    ОтветитьУдалить
  23. Статья действительно очень интересная и полезная, особенно новичку.

    У меня возник вопрос. Необходимо ли досконально снижать связанность классов? Как узнать, что пора остановиться?

    Допустим, в приведенном классе Reporter осталась зависимость от другого класса Report. Что с этим можно сделать?

    ОтветитьУдалить
  24. @

    Спасибо за вопрос. Предлагаю вам посмотреть статью http://en.wikipedia.org/wiki/Cohesion_(computer_science) и в своих проектах стремиться к функциональность связности элементов.

    ОтветитьУдалить
  25. Непонятно, почему этому принципу было дано название "Инверсия Зависимости"? Под "инверсией" нужно понимать изменение направления на обратное? На мой взгляд вы написали о том как этот принцип применять, но ясное, четкое представление об этом принципе не дали...

    ОтветитьУдалить
  26. Если по-простому, то обратите внимание на первый рисунок в разделе Инвертированная архитектура, а потом на второй. Заметили куда теперь направлены зависимости в коде и что зависимости прерываются на "абстрактных" слоях?

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

    ОтветитьУдалить
  28. День добрый, до меня не сразу дошел смысл названия и что конкретно инвертируется. Более того если попросить меня сформулировать у меня это не получится ) Не могли бы Вы написать о названии этого принципа и что конкретно инвертирует? А главное показать ход мыслей проектировщика согласно этому принципу? С примерами! ;)

    ОтветитьУдалить
  29. Alexander Byndyu5 мая 2012 г., 17:05

    Вроде уже показал с примерами, как было и как стало. Вы изучили код в статье?

    ОтветитьУдалить
  30. В ссылках лежит "Роберт Мартин: The Dependency Inversion Principle" - там он конкретно отвечает на вопрос по поводу названия (6 страница): "One might question why I use the word “inversion”..."

    ОтветитьУдалить
  31. Я тут в качестве белой вороны выступлю. Исходная задача вообще решается на процедурном языке. Решение задачи вводит аж 7 новых сущностей, что, как известно, на грани возможностей человека. А если ещё сущности есть? Введение новых абстракций решает все проблемы, кроме слишком большого числа абстракций. В результате имеем код, для понимания которого требуется существенно больше усилий, чем для понимания изначального. И все для того, чтобы в каком-то гипотетическом случае переписывать меньше тестов. В качестве (не очень доказательного) примера могу привести java компонент mime4j, который с точки зрения паттернов написан идеально, но для того, чтобы воспользоваться возможностью расширения в ООП для шаблонов, приходится наследовать 5 интерфейсов IВсякоеРазное... и копипэйстить кучу кода. То есть я считаю, что пока не указан конкретный содержательный юзкейс (с тестами и сравнением вариантов), обосновывающий введение новых абстракций, подобные надстройки только загромождают и запутывают программу. На мой взгляд, это ситуация короткого одеяла -- закрывая бороду, открываешь ноги.

    ОтветитьУдалить
  32. Вопрос по определению IoC.
    Зачем нужно оговаривать второй пункт ("Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций."), если это очевидное утверждение которое по-определению абстракции всегда выполняется?

    ОтветитьУдалить
  33. Круто когда автор пишет статью по ООП без привязки к технологии. Такие статьи потом можно перечитывать. За это вам большое спасибо! А то любят некоторые делать "на примерах" с какими-нибудь WPF.

    ОтветитьУдалить
  34. Артем Горбатюк3 мая 2015 г., 20:53

    День добрый, жаль что не увидел аргументированного ответа на поставленный вопрос товарищем 2 года назад.
    Я сейчас прибываю в состоянии изучения IoC и ни где не увидел ясных причин для ее использования.

    ОтветитьУдалить
  35. Всё, что может быть понято неправильно, будет понято не правильно. Поэтому лучше уточнить все отношения.

    ОтветитьУдалить
  36. Артем, выше постарался ответить. Если у вас остались вопросы, то буду рад помочь.

    ОтветитьУдалить
  37. Грибок - , вызываемый патогенными микробактериями, является одним из наиболее часто встречающихся заболеваний, характеризующихся широкой симптоматикой весьма неприятных проявлений. Подцепить эту заразную болезнь можно где угодно: в сауне, бассейне, на пляже а также любом другом месте общественного пользования. Несмотря на тот факт, что в продаже сейчас имеется нескончаемое множество разнообразных аптечных мазей и гелей, до появления на рынке лечебного комплекса Варанга за короткий срок покончить с болезнетворными микроорганизмами, которые влияют не только на внешний вид ногтей и кожные покровы, но и наносят внушительный ущерб тканям, было очень тяжело. Благодаря появлению этого универсального крема, с непревзойденным натуральным составом, ликвидация грибка теперь не является трудной задачей. Не занимает много времени и не требует, как раньше, множества усилий. Вылечить грибок стоп можно будет всего за месяц использования мази. VarangaOfficial - варанга официальный сайт - все, что нужно знать об этом препарате. Воспользовавшись нашим онлайн-сервисом, вы получите возможность узнать исчерпывающую информацию об этом лекарственном средстве. Увидеть данные о проведенных клинических тестированиях, прочесть отзывы реальных покупателей и врачей. Изучить инструкцию по применению, прочесть об особенностях и методах работы комплекса, осмыслить, почему крем Варанга настолько эффективен, где можно заказать оригинальный препарат и, как не нарваться на фальсификат. Мы очень тщательно и скурпулезно проверяем размещаемые данные. Предоставляем пользователям нашего ресурса сведения, которые берутся только из достоверных источников. Если вы обнаружили признаки появления грибкового заболевани или уже довольно продолжительное время, без ощутимых результатов пытаетесь избавиться от этого досадного недуга, наш сайт покажет вам быстрый и простой способ устранения проблемы. Присоединяетесь и живите полноценной, здоровой жизнью. Мы собрали ответы на все вопросы на одном информационном ресурсе.

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

    ОтветитьУдалить
  39. Иногда может быть момент-подвернется работа мечты, изготовление аттестатов за 9 класс Однако в процессе регистрации требуется документ. Наша компания предоставляет возможность приобрести сертификат медсестры, зарегистрированной в государственном реестре, или сертификат фармацевтического консультанта по продажам. Сотрудники организации учитывают на чеку все эти тенденции и призваны изготовить и выдать спортсменам только лучший экземпляр, который соответствует всем техническим стандартам и помещен в официальный список россии. Мы изготовим сертификат об окончании курса любого тюменского техникума или техникума, а заодно и средней школы в других городах россии. Мы продаем дипломы установленного образца. Советник может выбрать популярный проект, а кроме того, создать карьерную стратегию в вашу пользу. Подобная документация также необходима специалистам в соседних областях. В сотрудничестве с нашей компанией у вас будут документы, которые характеризуются качеством и успешно не приведут ни к малейшим сложностям. Сейчас требования к уровню бумаг такого объема немного изменились, и в то же время возникает проблема с поиском качественной продукции, которая будет выпускать необходимую бумагу сразу. Также здесь протянут руку помощи правильная подготовка краткого обзора, уверенный, грамотный разговор на период собеседования и самые благоприятные манеры. Поэтому при печати мы будем использовать архивные бланки из нашей отличной информационной базы. Чтобы не терять ни минуты времени, средств на такое производство, получите почетную грамоту от сотрудников нашей компании. И в-третьих, вы никогда не узнаете свою собственную цену на собеседование, предъявив сертификат низкого уровня, и поэтому мы, насколько продукция, предоставившая вам некачественный сервис, также будем чувствовать себя в наших данных неуверенно. Здесь можно заказать сертификат специалиста любого института, вы можете быть в этом уверены, он четко соответствует стандартам данного документа. Хотя обращайте внимание не только на цены, но и на консультанта, так как он лучше "плавает" в этой области. После получения нескольких кандидатов выбор предоставляется именно тому человеку, который имеет официальное обоснование профессионализма. Не можете найти столь необходимый диплом или стесняетесь показать его близкому человеку? Как только подвиг окажется "с вами", информация о клиенте будет немедленно устранена. Первый шаг: возможность тратить экономно. Например, можно купить сертификат во, заплатив 16-20 тысяч рублей. Ведь вы находитесь далеко от нашего сайта, значит, знаете, как заказать акт. Не безопаснее ли было бы сделать разумный вариант, прибегнуть к помощи нашей организации? Вам не нужно прятать идею покупки под сукно, но вы можете каждую секунду менять свою собственную жизнь к лучшему будущему. С такой корочкой у вас есть возможность выйти на позицию без каких-либо проблем, чтобы не беспокоиться о ее качестве. Вы полностью подходите ей по своим знаниям и навыкам, но у корпорации есть центральный принцип: принимать в свои ряды только компетентных сотрудников. Вы будете уверены в справедливости сказанного.

    ОтветитьУдалить
  40. Не полный перечень учитывает какие-либо важные факторы, часы и минуты и право выдачи такого документа общепринятым методом, купить диплом в липецке http://goz-diplom.com/diplomy-v-lipecke/ Поэтому наша компания предоставляет клиентам качественную альтернативу. И такой момент-тоже обычное обстоятельство, так как не все хотят тратить драгоценные часы на стандартное образование. Относительно легко приобрести сертификат профессионального училища в санкт-петербурге. Петербург и в кратчайшие сроки, при необходимости заполнив форму запроса или позвонив по указанному номеру. Это не слишком безопасная работа по изготовлению диплома пту 2014 года или всех остальных лет, требующая от представителей сильного пола действительно больших затрат. Московское профессиональное училище № 190 и "мокрая" печать профессионального училища. Форма и конфигурация бланка, приобретенного из диплома московского профессионального училища № 190, будет точно совпадать с годом выпуска. Решили ли вы стать обладателем соответствующего документа московского профессионального училища № 190, наконец-то такого же, который предоставляется в этом вузе? Чтобы выдать необходимый аттестат, нужно учиться в том же институте с тем же названием в течение тех же трех лет, пройти множество академических форм контроля, составить и предупредить выпускной план. Изучив наш всесторонний выбор изготовленных квитанций или счетов-фактур, вы, несомненно, легко найдете идеальный вариант для вашего диплома, и он может вас не ущипнуть. Важно, что мы даем гарантию, что аттестат по липецку будет хорошим, качественным, установленного плана. Акт такого формата естественно помогает вам с бюрократическим трудоустройством, за исключением того, что место не нуждается в поддержке всего образовательного процесса. Но мы поможем пользователям с его секретами, а любителям кино представим качественный, количественный скачок! Для того чтобы получить желаемый документ, вы охотно прибегнете к нашим услугам. Если по каким-либо причинам вам не удалось закончить колледж в прошлом году, но у вас есть возможность просто приобрести документ пту предыдущего года в ежедневной мастерской. И если обойти аферу ужасно сложно и нет никого, кто стоит за приглашениями во всемирной паутине, то приобретение сертификата "вышки" в подземном переходе-это плохое мнение и вина за ошибку полностью лежит на человечестве, которое пошло на это. При этом мы абсолютно уверены в качестве наших услуг. Для этого вам нужно всего лишь заполнить специальную анкету на портале, на последнем этапе сотрудник нашей компании позвонит вам, предоставит квалифицированную информацию относительно интересующего документа, ответит на возникшие вопросы, если возникнут какие-либо трудности, поможет определиться с правильным вариантом. Мы гарантируем, что цена одного выбранного сертификата зависит от вложений в бланки, печать на дорогом офсетном принтере, установку штампов и автографов, оплату труда мастеров нашей организации и консультантов-менеджеров. У нас развита широкая клиентская база в пределах границ российской федерации, с отзывами о нашей работе вы всегда можете ознакомиться в интернете.

    ОтветитьУдалить
  41. Добропорядочная контора не просит, а потому и не получает никаких денег до того, как техникума дипломы Клиент получит товар. Благодаря двадцатилетнему опыту работы команды good-diploms и постоянному наличию всех соответствующих типов бланков производственный сертификат на 11 класс занимает здесь 2 дня, а стоят они исключительно в огромной очереди. Смогу ли я принять участие в олимпиадах и окажут ли влияние преимущества поступления на олимпиадные дипломы, если у меня уже был заграничный паспорт 11-й категории, то есть я уже пересмотрел свое среднее дневное школьное образование перед участием в олимпиадах? Это разрешение имеет много бонусов, потому что позволяет иметь соответствующую школу раньше своих одноклассников. То ли курьерская служба привезет вашему автомобилю сообщение, то ли у вас будет час на почте - кто забирает закон, чтобы сначала распечатать и проверить, а уже потом решать, за что платить, а за что нет. Диплом будет полезен вам при оформлении образования, иногда его запрашивают у работодателей или работодателя при приеме на работу, чтобы присмотреть оценки по существующим дисциплинам). В любую точку страны россии, да и ближнего зарубежья, письма идут гораздо дольше, в связи с этим мы тоже идем вместе с вами сюда - выбирайте цветочный магазин сами, а иногда, тогда эффект может быть оперативным. У нас еще достаточно советских, а также давних российских бланков прошлого времени, но нет никакой проблемы разработать абсолютно новый заграничный паспорт! Хотя у меня уже есть загранпаспорт на 11 класс. На основании аттестата по 9-му разряду я заключаю договор на заочное обучение в первом классе частной школы (потому что больше не могу учиться бесплатно). Это даст все основания для того, чтобы документ выдержал любое испытание, поскольку наш диплом отличается от того, который применяется в высшем учебном заведении, просто потому, что посетитель не провел годы, сидя за письменным столом. Мы обслуживаем без предоплаты. Некоторые дилеры, получив предоплату, только перестают ходить на переплет, а покупатель остается в недоумении. Если вы находитесь в столице или московской области, то вам поможет отработать наш отдельный курьер-в те дни это делается быстро, плюс еще и до указанной точки, включая парки, остановки метро и т.Д. Чтобы не" сжечь " вашу родную почту. Поэтому вы можете сэкономить деньги, если только не согласитесь приобрести наши корочки, которые окупятся в первые две - три недели деятельности в незнакомом месте. Клиент проверяет уровень. Независимо от того, как именно заказ попадает к получателю, вы можете измерить деталь перед расчетом. И мы дадим вам удобную возможность купить это время в подобии города, а также в другом районе. Если ваш звонок не слишком дорогой в соответствии с пунктами ваших тарифов, заполните окно обратного вызова, чтобы наши курьеры позвонили вам в следующий раз!

    ОтветитьУдалить
  42. Наша корпорация предоставляет услугу по изготовлению документации об образовании до самой смерти. Если вы сами уверенно показали свою силу в роли специалиста, изготовление дипломов академии Организация способна прибавить себе зарплату даже назначив менеджера. Если 59 процентов опрошенных уверены, что нижеследующее недопустимо в любом случае, то 23 процента считают, что в некоторых ситуациях допустимо распоряжаться поддельной почетной грамотой. Соответственно, вы знаете, где купить диплом колледжа. Можно ли проучиться 4 года, стать обладателем степени бакалавра, найти работу и решить, какая отрасль деятельности полностью отличается от какой было бы неплохо? Студенты часто делают заметки, слушают лекции и не в состоянии понять, насколько вся эта информация поможет вам в вашей будущей профессии. Сделать утерянный документ можно, но только в том мегаполисе, где проходила тренировка. При этом вы не только освободитесь от необходимости ходить на скучные пары, но и получите возможность устроиться на работу по умолчанию, вы не будете читать о стрессе, который часто преследует студентов во время сдачи сессии. Знаете, чтобы образование в институте не оправдывало вложенных времени и денег. Вы хотите найти работу мгновенно, чтобы взять бесценную изощренность – и многолетний опыт. Так что же именно нужно делать, если вам нужен надежный и престижный диплом при поступлении на работу, дальнейшем продвижении по службе или, возможно, при открытии собственной организации? Сильное желание дать навыки и научиться каким-то новым и полезным навыкам-это цель, которая должна ощущаться внутренне, а также не всегда содержать в себе специализированную школу. Несмотря на это, выдвигается ключевое требование - помочь еще одному в институте. Действительно получить рыжеволосый сертификат с отличными оценками. Парень, большое тебе спасибо!, Я заказал диплом школы, выданный в июне 2016 года. Я пишу описание с благодарностью за производимую работу, потому что прошел собеседование с 1 офисом с дипломом. Школьные сочинения по окружающему миру скачать школьные сочинения по окружающему миру скачать школьные сочинения скачать школьные сочинения зарплата по объекту аттестат имитационного моделирования. Если сравнить "положительные и отрицательные стороны" в современном состоянии российского образования, то выяснилось, почему заказ бакалавриата гораздо интереснее с точки зрения прибыли и затрат. По сравнению с порядком разработки: заказать почетную грамоту недорого. Еще один банальный оттиск 60 страниц диплома стоит очень дорого. В том числе и в дорогих московских вузах, преподаватели не в состоянии уступить самому главному - практике. Когда такие условия устраивают клиента, вы можете начать работать честно уже сегодня. Вы думаете о смене позиций. Безопасность информации будет обеспечиваться за счет использования блокчейн-технологий и кода данных",-пояснил вице-министр минобрнауки. Особенно когда студент переезжает на обучение в другой регион или переводится на платную форму. В академии работают доктора и кандидаты наук, которые также преподаются институтом в ведущих университетах страны.>

    ОтветитьУдалить
  43. Решить проблему, во сколько обойдется диплом медсестры, мы никогда не сможем так долго, все равно не увидим непосредственно обозначенный нами. Отсутствие законного подтверждения профессионального образования значительно снижает ваши конкурентные преимущества перед другими абитуриентами. Во сколько нам обойдется сертификат во? Во сколько вам обойдется сертификат о высшем образовании? Теперь он снимает такие вопросы, чтобы доесть: можно просто приобрести документ института установленного образца. Кому нужен был почетный диплом о высшем образовании? Заказать сертификат о высшем образовании в нашей стране также невозможно. Купить документ в нашей компании совсем не сложно. Мы можем гарантировать, что с нами сертификат приведет к самому высокому уровню. Мы осознаем, что наша реальность серьезна, поэтому предлагаем за нее солидную плату. Мы поможем вам ответить на ваши вопросы оперативно, по разумной цене и с гарантией. В этой ситуации, если вам не нужно купить почетную грамоту немедленно, вы можете воспользоваться услугами нашей организации. Напоминаем, что мы можем создать документ любого брянского профессионального училища, если нужного вам учебного заведения нет в арсенале вузов, перечисленных на странице. Мы продаем государственный сертификат образца требуемого периода. В случае необходимости наши специалисты с готовностью помогут с выбором подходящего техникума и дадут ответы на любые возникшие у вас вопросы. Часто в конце письменной процедуры руководитель исследования навязывает свою точку зрения, заставляя его корректировать заранее согласованную во многих моментах программу. Часто действительно можно нарваться на эту неприятность, так как не хватает свободных кейсов для эксклюзивного профиля работы. Купите сертификат института, купите соответствующий документ, а возможно, вы не уверены, что закажете корочку в санкт-петербурге, столице россии или другом мегаполисе-купите сертификат санкт-петербургского диплома по запросу. А наличие специального оборудования упрощает процедуру выдачи сертификата во: примерно, изготовление одного диплома по личному проекту длится около нескольких дней с момента подачи заявки. Впоследствии он предоставил коллективу диплом чувашского государственного университета и поступил на заочное отделение, из которого в 2005 году перевелся на кафедру нижегородской академии мвд, где ему выдали последний документ по профилю "юридическая наука". В нашем интернет-магазине вы можете купить сертификат российского вуза, сэкономить часы, минуты и силы, а также получить приемлемый результат процедуры без сложных манипуляций и бюрократии. Кстати, никто не в состоянии посвятить эти часы обучению, поэтому импульс к получению почетной грамоты обычно появляется у пользователей, которые пытаются найти достойную должность, имея в биографии огромный опыт работы. И даже если у вас есть навыки и компетенции, вы не будете рассчитывать на престижную зарплату, так как не сможете подтвердить свой профессиональный уровень соответствующей "корочкой". Что от вас требуется? Что вы будете делать, когда его нет в наличии, вам не нужно подтверждать его соответствующими официальными документами, или вы пропустили минуту на обучение? Если вам понравился этот пункт, и зритель думает получить подробную информацию о дипломы спб цены Сверху, дружелюбно взгляните на этот сайт.

    ОтветитьУдалить
  44. Почетная грамота может быть доказательством того, что выпускник вуза освоил программу зап и успешно сдал самые необходимые экзамены, а главное, дипломы о высшем образовании Ему удалось написать и закрепить итоговую диссертацию. К сожалению, в процессе обращения в загс с просьбой о дубликате он получает отрицательный ответ. Кроме того, семейная история не дает возможности тратить месяцы и годы на учебу. Приятное ощущение для комиссии производит также и возможность получения так называемого мини-сертификата о высшем образовании по заявке в санкт-петербурге для всех заинтересованных членов комиссии, почему и расскажет об уважительном подходе через сайт. Наши менеджеры оформят заказчику законный документ колледжа "колледж вакансий" на реальном листе гознака, с максимально возможным соблюдением всех уровней безопасности, в том числе: сериалы и номера бланки, наличие голограмм, водяных знаков и микротекста нужного шрифта и размера. Наши специалисты исчерпывающе предоставят любую актуальную информацию, чтобы вы могли легко купить сертификат института. Если вам нужно малиновое письмо на заказ в нашем городе, все это наши специалисты помогут клиенту с принятием заказа. В тот момент, когда запрос будет готов, наш курьер доставит его клиенту самостоятельно своими руками. Для работы у нас, для получения диплома, все, что от нас зависит, чтобы занять руководящие должности, диплом техникума может быть не особенно актуален. Как если бы наш магазин обеспечивал перевозку по рф, насколько только курьер, а также заказ по почте, каждый определяется тем, насколько это удобнее для вас. Целостность в общих требованиях, помимо той, которая не сужает, а, скорее, правильнее, предполагает творчество студента в первую очередь при разработке темы его диссертации. В наше время дипломы предлагают огромное количество компаний, но набрав в поисковой строке "дипломы учителя" не нужно заглядывать ни на одну страницу. Есть двадцатилетний навык работы в больнице в преемственности медсестры, также можно, правда, сменить медсестру, есть привлекательная вакансия, но нет документа о соответствующем образовании. И не ошибитесь среди обилия компаний, которые помогут сделать сертификат определенного образца, следует помнить свои собственные правила успешного сотрудничества. 4. Обеспечьте себе собственные корочки и оплатите товар. В совершенствовании есть и преимущество в использовании решения заказать почетную грамоту пту в ижевске. Вы хотите проверить корочку индивидуально – и, убедившись в ее качестве, заработать деньги. Однако, когда у вас есть сомнения по поводу всего, вы можете обратиться к опытным специалистам круглосуточно, которые все исправят, почему вы не знаете, что не так. К таким отраслям относится документ высшей школы экономики или магистерская диссертация, а на какое-то время даже сертификат специалиста. Это верно для всех этих аспектов ит. Вне различных категорий и типов с областями написания дипломов, в магазине даже очень требовательные также представлены стандартные данные о рынке актуальности и получении четкой, логичной структуры. Особенно если учесть, почему практически есть шанс купить степень магистра в родном городе, а оформить структуру в любом другом мегаполисе страны.

    ОтветитьУдалить
  45. Вы можете приобрести документы с занесением в базу данных с доставкой вообще в вашу родную страну-просто позвоните по круглосуточному номеру 7(499) 229-77-55. Следует отметить один важный факт. Тем пользователям ресурса, которые хотят иметь в наличии надежный документ, следует сразу же обратить пристальное внимание на центр, где они собираются приобрести документы с реестром в городе. Москва. Обратите свое внимание на исполнителей, которые в свое время доказали свою прибыльность на валютном рынке после оказания таких услуг. Компании, предлагающие заказать диплом без записи его в базу данных, обязаны соответствовать определенным стандартам. Купить диплом. Регистрация. Диплом с проводкой. Покупка корочки с занесением в базу данных. Вы можете приобрести документы со сделкой в городе. Москва с тренировками. С помощью этой опции реестр пополняется в течение 1 года. Все указанное выглядит так. Вы можете заключить договор на фиктивное обучение, и очень, очень важно иметь академический документ на него, вы якобы будете зачислены в любой удобный вуз. Это дает возможность параллельно заниматься обычными делами, и вам не нужно думать об остальном. Итак, сначала происходит перевод на последние курсы, для клиентов, проходящих занятия, даже в конце года вы получите оригиналы документов с занесением в государственный реестр такого учебного заведения. Но но нужно учитывать некоторые тонкости: 1. Документ сдается в эксплуатацию в конце года. Но в том случае, если это срочно необходимо, можно приобрести диплом с регистрацией в госреестре в специализированной организации. 2. Это, конечно, подразумевает выбор надежного производства, которое сможет выполнить профессию досконально. Обратитесь по указанному выше номеру телефона для приобретения диплома без приложения к общему реестру и обсуждения любых вопросов по изготовлению таких документов. Можно купить справку с записью в архив в екатеринбурге, москве, даже позже. Как обычно, такая услуга собирается исключительно в крупных фирмах. Ведь мало какие организации выработали достаточную компетенцию для выполнения такой сложной операции. На цену корок влияют различные факторы. Часть договоренности необходима заинтересованной компании. Когда такой товар мошенничает, комики могут предложить приобрести сертификат во с записью в реестре ниже себестоимости, совсем не отличного качества. Поэтому когда такой сертификат в надежном формате необходим, то для его изготовления требуется подождать определенное время. Позвонить в соответствующую компанию, затем приобрести диплом с регистрацией в госреестре, и обратиться за консультацией по любым существующим сложностям. Гарантии получения соответствующей документации. Вход в базу данных. Когда нужно среднее образование, а этих часов для получения диплома нет в наличии, то покупка его является хорошим проверенным решением проблемы геморроя. Наличие корочки дает вам шанс получить зарплату больше, чем символы там, где она отсутствует. Любые документы мы вносим в базу данных, поэтому вы можете быть уверены в ее подлинности. Предлагаемые документы обладают всеми качественными характеристиками. Если вам нужны дипломы колледжа, лицея или пту, то звоните смело.С этой корочкой у кого-то все получится!, Если у вас есть какие-либо вопросы о том, где и в какой степени вы можете использовать диплом магистра купить http://meganauka.com/, Вы можете позвонить нам на наш веб-сайт.

    ОтветитьУдалить