Модульность WPF-приложений (или Использование IoC-контейнеров, часть 3)

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

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

Итак, что у нас имеется? Допустим, есть модуль, который призван искать подключенные к компьютеру устройства — SD-карты с определенным содержимым, или приборы, подключенный по USB.
Как бы мы хотели видеть использование данного класса?

var detector = new DeviceDetector();
var devices = detector.Detect();

И если вторая строчка определяется контрактом класса и фактически так и выглядит, то вот конструктор класса DeviceDetector выглядит совсем по-другому:

public class DeviceDetector {
  public DeviceDetector(IComPortSearcher searcher,
                                IUsbValidator validator,
                                IDriveSearcher driveSearcher,
                                IDeviceValidator validator) {
  }
}
var devices = detector.Detect();

Continue reading

Использование IoC-контейнеров, часть 2

Со времен прошлой записи про Inversion of Control, контейнеры таки плотно вошли и пустили корни в нашей повседневной жизни. В новом проекте он используется сплошь и рядом, да и при переписывании частей legacy-кода то и дело норовит внедриться.

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

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

Как «рецепт успеха» я бы рекомендовал всегда рассматривать проект (или группу проектов с единым назначением) как обособленную единицу, и всегда задумываться о его собственном внешнем интерфейсе (он, обычно, состоит из буквально одного-двух интерфейсов), а не отталкиваться от того, как он будет использован в конкретный момент в конкретном продукте.

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