- PVSM.RU - https://www.pvsm.ru -
Оглавление
- Введение [1]
- Инициализация приложений Prism [2]
- Управление зависимостями между компонентами [3]
- Разработка модульных приложений
- Реализация паттерна MVVM
- Продвинутые сценарии MVVM
- Создание пользовательского интерфейса
- Навигация
- Способы коммуникации между слабосвязанными компонентами
Приложения, основанные на библиотеке Prism, являются составными приложениями, потенциально состоящими из слабо связанных служб и компонент. Они должны взаимодействовать друг с другом так, чтобы предоставлять содержание пользовательскому интерфейсу и получать уведомления о действиях пользователя. Поскольку они слабо связаны, для обеспечения необходимой функциональности, необходим способ их взаимодействия и связи друг с другом.
Для связи всех частей воедино, Prism приложения полагаются на DI контейнер. DI контейнеры уменьшают зависимости между объектами, предоставляя способ создания экземпляров классов и управления их временем жизни в зависимости от конфигурации контейнера. При создании объектов с помощью контейнера, он инжектирует в них необходимые зависимости. Если зависимости еще не были созданы, то контейнер в начале создает их и разрешает их собственные зависимости. В некоторых случаях, сам контейнер внедряется как зависимость. Например, при использовании Unity, в модули внедряется контейнер для того, чтобы они могли зарегистрировать в нём свои представления и службы.
Есть несколько преимуществ использования контейнера:
В контексте приложения, основанного на библиотеке Prism, есть определенные преимущества контейнера:
Заметка
Некоторые образцы в руководстве Prism используют контейнер Unity Application Block (Unity). Другие, например Modularity QuickStarts, используют Managed Extensibility Framework (MEF). Библиотека самого Prism не зависима от применяемого контейнера, и вы можете использовать её службы и паттерны с другими контейнерами, такими как CastleWindsor, Autofac, StructureMap, Spring.NET, или с любым другим.
Библиотека Prism предоставляет два DI контейнера по умолчанию: Unity и MEF. Prism расширяема, таким образом предоставляется возможность использовать другие контейнеры, написав немного кода для их адаптации. И Unity, и MEF обеспечивают одинаковую основную функциональность, необходимую для внедрения зависимостей, даже учитывая то, что они работают сильно по-разному. Некоторые из возможностей, предоставляемые обоими контейнерами:
Unity предоставляет несколько возможностей, которых нет в MEF:
MEF предоставляет несколько возможностей, которых нет в Unity:
Контейнеры имеют различия в возможностях и работают по-разному, но библиотека Prism будет работать с любым контейнером и обеспечивать одинаковую функциональность. Рассматривая, какой контейнер использовать, имейте в виду свой предыдущий опыт и определите, какой контейнер лучше вписывается в сценарий вашего приложения.
Что следует рассмотреть перед использованием контейнеров:
Заметка.
Некоторые контейнеры способны разрешать экземпляры объектов почти так же быстро, как и их создание через ключевое слово new. Но, в любом случае, разрешение через контейнер большого количества объектов в цикле, должно быть серьёзно обосновано.
Заметка
Некоторые контейнеры, такие как MEF, не могут быть сконфигурированы через конфигурационный файл и должны быть сконфигурированы в коде.
Контейнеры используются для двух основных целей, а именно: регистрация и разрешение.
Прежде, чем можно будет внедрить зависимости в объект, типы зависимостей должны быть зарегистрированы в контейнере. Регистрация типа обычно включает передачу контейнеру интерфейса и конкретного типа, который реализует этот интерфейс. Есть, прежде всего, два средства для того, чтобы зарегистрировать типы и объекты: в коде, или через файл конфигурации. Детали реализации могут изменяться в зависимости от контейнера.
Как правило, есть два способа зарегистрировать типы и объекты в контейнере в коде:
Во время инициализации, тип может зарегистрировать другие типы, такие как представления и службы. Регистрация позволяет разрешать их зависимости контейнером, и позволяет им стать доступными другим типам. Чтобы сделать это, необходимо внедрить контейнер в конструктор модуля. Следующий код показывает, как OrderModule из Commanding QuickStart регистрирует тип репозитория при инициализации, как синглтон.
public class OrderModule : IModule
{
public void Initialize()
{
this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
...
}
...
}
В зависимости от того, какой контейнер вы используете, регистрация может также быть выполнена вне кода через файл конфигурации. Для примера смотрите, «Registering Modules using a Configuration File» в Главе 4, "Modular Application Development [4]."
Для регистрации типов в контейнере, MEF использует систему, основанную на атрибутах. В результате довольно легко добавить регистрацию типа к контейнеру: для этого требуется добавить атрибут [Export] к экспортируемому типу, как показано в следующем примере.
[Export(typeof(ILoggerFacade))]
public class CallbackLogger: ILoggerFacade
{
}
Другим вариантом использования MEF, может быть создание экземпляра класса и регистрация именно этого экземпляра в контейнере. QuickStartBootstrapper в Modularity for Silverlight with MEF QuickStart показывает пример этого в методе ConfigureContainer.
protected override void ConfigureContainer()
{
base.ConfigureContainer();
// Поскольку мы создали CallbackLogger, и он должен использоваться сразу,
// мы проводим его композицию, чтобы удовлетворить любой импорт, который он имеет.
this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger);
}
Заметка
При использовании MEF как контейнера рекомендуется, чтобы вы использовали именно атрибуты для регистрации типов.
После того, как тип зарегистрирован, он может быть разрешен или внедрён как зависимость. Когда тип разрешается, и контейнер должен создать новый экземпляр, то он внедряет зависимости в этот экземпляр.
Вообще, когда тип разрешается, происходит одна из трех вещей:
Заметка
Некоторые контейнеры, включая Unity, позволяют разрешать конкретный тип, который не был зарегистрирован.
Заметка
По умолчанию, типы, зарегистрированные в MEF, являются синглтонами, и контейнер хранит ссылки на объекты. В Unity, по умолчанию, возвращаются новые экземпляры объектов, и контейнер не сохраняет на них ссылок.
Следующий пример кода из Commanding QuickStart показывает, как представления OrdersEditorView и OrdersToolBar разрешаются из контейнера для привязки их к соответствующим регионам.
public class OrderModule : IModule
{
public void Initialize()
{
this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
// Показ представления Orders Editor в главном регионе оболочки.
this.regionManager.RegisterViewWithRegion("MainRegion",
() =>; this.container.Resolve<OrdersEditorView>());
// Показ представления Orders Toolbar в регионе панели инструментов.
this.regionManager.RegisterViewWithRegion("GlobalCommandsRegion",
() => this.container.Resolve<OrdersToolBar>());
}
...
}
Конструктор OrdersEditorPresentationModel содержит следующие зависимости (репозиторий заказов и прокси команды заказов), которые вводятся, когда он разрешается.
public OrdersEditorPresentationModel(IOrdersRepository ordersRepository, OrdersCommandProxy commandProxy)
{
this.ordersRepository = ordersRepository;
this.commandProxy = commandProxy;
// Создание фиктивных данных о заказе.
this.PopulateOrders();
// Инициализация CollectionView для основной коллекции заказов.
#if SILVERLIGHT
this.Orders = new PagedCollectionView( _orders );
#else
this.Orders = new ListCollectionView( _orders );
#endif
// Отслеживание текущего выбора.
this.Orders.CurrentChanged += SelectedOrderChanged;
this.Orders.MoveCurrentTo(null);
}
В дополнение к внедрению в конструктор, как показано в предыдущем примере, Unity также может внедрять зависимости в свойства. Любые свойства, к которым применён атрибут [Dependency], автоматически разрешаются и внедряются, при разрешении объекта. Если свойство помечено атрибутом OptionalDependency, то при невозможности разрешить зависимость, свойству присваивается null, а не вбрасывается исключение.
Следующий пример кода показывает, как Bootstrapper в Modularity for Silverlight with MEF QuickStart получает экземпляр оболочки. Вместо того, чтобы запросить конкретный тип, код мог бы запросить экземпляр интерфейса.
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<Shell>();
}
В любом классе, который разрешается MEF, можно также использовать инжекцию в конструктор, как показано в следующем примере кода из ModuleA в Modularity for Silverlight with MEF QuickStart, у которого внедряются ILoggerFacade и IModuleTracker.
[ImportingConstructor]
public ModuleA(ILoggerFacade logger, IModuleTracker moduleTracker)
{
if (logger == null)
{
throw new ArgumentNullException("logger");
}
if (moduleTracker == null)
{
throw new ArgumentNullException("moduleTracker");
}
this.logger = logger;
this.moduleTracker = moduleTracker;
this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA);
}
С другой стороны, можно использовать инжекцию свойства, как показано в классе ModuleTracker из Modularity for Silverlight with MEF QuickStart, у которого есть экземпляр внедряемого ILoggerFacade.
[Export(typeof(IModuleTracker))]
public class ModuleTracker : IModuleTracker
{
// Из-за ограничений Silverlight/MEF, поле должно быть общедоступно.
[Import] public ILoggerFacade Logger;
}
Заметка
В Silverlight импортируемые свойства и поля должны быть общедоступными.
Контейнеры внедрения зависимости, часто называемые как «контейнеры», используются, чтобы удовлетворить зависимости между компонентами. Удовлетворение этих зависимостей обычно включает регистрацию и разрешение. Библиотека Prism предоставляет поддержку для контейнеров Unity и MEF, но не зависит от них. Поскольку библиотека имеет доступ к контейнеру через интерфейс IServiceLocator, контейнер может быть легко заменён. Чтобы сделать это, он должен реализовать интерфейс IServiceLocator. Обычно, если вы замените контейнер, то вы должны будете также написать свой собственный контейнерно-специфичный загрузчик. Интерфейс IServiceLocator определяется в Common Service Locator Library. Это open source проект по обеспечению абстракции контейнера IoC (Inversion of Control), таких как контейнеры внедрения зависимостей, и локаторы службы. Цель использования этой библиотеки состоит в том, чтобы использовать IoC и Service Location, без предоставления определённой реализации контейнера.
Библиотека Prism предоставляет UnityServiceLocatorAdapter и MefServiceLocatorAdapter. Оба адаптера реализуют интерфейс ISeviceLocator, расширяя тип ServiceLocatorImplBase. Следующая иллюстрация показывает иерархию классов.
Хотя библиотека Prism не ссылается и не полагается на определенный контейнер, для приложения характерно использовать вполне конкретный DI контейнер. Это означает, что для определенного приложения разумно ссылаться на определённый контейнер, но библиотека Prism не ссылается на контейнер непосредственно. Например, приложение Stock Trader RI и несколько из QuickStarts, используют Unity в качестве контейнера. Другие примеры и QuickStarts используют MEF.
Следующий код показывает интерфейс IServiceLocator.
public interface IServiceLocator : IServiceProvider
{
object GetInstance(Type serviceType);
object GetInstance(Type serviceType, string key);
IEnumerable<object> GetAllInstances(Type serviceType);
TService GetInstance<TService>();
TService GetInstance<TService>(string key);
IEnumerable<TService> GetAllInstances<TService>();
}
Service Locator дополняет библиотеку Prism методами расширения, показанными в следующем коде. Можно увидеть, что IServiceLocator используется только для разрешения, а не для регистрации.
public static class ServiceLocatorExtensions
{
public static object TryResolve(this IServiceLocator locator, Type type)
{
try
{
return locator.GetInstance(type);
}
catch (ActivationException)
{
return null;
}
}
public static T TryResolve<T>(this IServiceLocator locator) where T: class
{
return locator.TryResolve(typeof(T)) as T;
}
}
Метод расширения TryResolve, который контейнер Unity не поддерживает, возвращает экземпляр типа, который должен быть разрешен, если он было зарегистрирован, иначе он возвращает null.
ModuleInitializer использует IServiceLocator для того, чтобы разрешить зависимости модуля во время его загрузки, как показано в следующих примерах кода.
IModule moduleInstance = null;
try
{
moduleInstance = this.CreateModule(moduleInfo);
moduleInstance.Initialize();
}
...
protected virtual IModule CreateModule(string typeName)
{
Type moduleType = Type.GetType(typeName);
if (moduleType == null)
{
throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));
}
return (IModule)this.serviceLocator.GetInstance(moduleType);
}
IServiceLocator не предназначается для использования в качестве контейнера общего назначения. У контейнеров может быть различная семантика использования, которая часто влияет выбор контейнера. Принимая это во внимание, Stock Trader RI использует контейнер внедрения зависимости непосредственно вместо того, чтобы использовать IServiceLocator. Это является рекомендованным подходом при разработке приложений.
В следующих ситуациях использование IServiceLocator является уместным:
Для получения информации, связанной с DI контейнерами, смотрите:
Автор: Unrul
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/interfejsy/32618
Ссылки в тексте:
[1] Введение: http://habrahabr.ru/post/176851/
[2] Инициализация приложений Prism: http://habrahabr.ru/post/176853/
[3] Управление зависимостями между компонентами: http://habrahabr.ru/post/176861/
[4] Modular Application Development: http://msdn.microsoft.com/en-us/library/gg405479(v=PandP.40).aspx
[5] http://www.msdn.com/unity: http://www.msdn.com/unity
[6] http://www.codeplex.com/unity: http://www.codeplex.com/unity
[7] http://msdn.microsoft.com/en-us/library/dd460648.aspx: http://msdn.microsoft.com/en-us/library/dd460648.aspx
[8] http://mef.codeplex.com/: http://mef.codeplex.com/
[9] http://www.martinfowler.com/articles/injection.html: http://www.martinfowler.com/articles/injection.html
[10] http://msdn.microsoft.com/en-us/magazine/cc163739.aspx: http://msdn.microsoft.com/en-us/magazine/cc163739.aspx
[11] http://msdn.microsoft.com/en-us/magazine/cc337885.aspx: http://msdn.microsoft.com/en-us/magazine/cc337885.aspx
[12] http://www.castleproject.org/container/index.html: http://www.castleproject.org/container/index.html
[13] http://structuremap.sourceforge.net/Default.htm: http://structuremap.sourceforge.net/Default.htm
[14] http://www.springframework.net/: http://www.springframework.net/
Нажмите здесь для печати.