Leveraging Dependency Injection(DI) in Universal Applications - Tamir Dresher
-
Upload
tamir-dresher -
Category
Software
-
view
97 -
download
4
Transcript of Leveraging Dependency Injection(DI) in Universal Applications - Tamir Dresher
Tamir Dresher
Senior Software ArchitectMarch 2015
Leveraging Dependency Injection(DI) in Universal Application
About Me
• Software architect, consultant and instructor
• Software Engineering Lecturer @ Ruppin Academic Center
• Technology addict
• .NET and Native Windows Programming
@[email protected]://www.TamirDresher.com.
How do we get the dependencies
• Instantiating
– I must know the concrete class
– What if that class has dependencies of its own?
• Using a Factory Class
– Now I have a dependency on the Factory
• Using a Singleton
– Now I have a Dependency on the singleton
– Now I must know that it is a singleton
5
Example
7
public MainPageViewModel(){
_lectureManager = new LectureManager();var rootFrame = Window.Current.Content as Frame;_navigationService = new NavigationService(rootFrame);Lectures = new ObservableCollection<LectureDay>();
}
Example – Deeper in the chain
8
public LectureManager(){
_mdevconService = new MdevconService.MdevconService();}
Universal Applications
• Sharing Code is fun
• Sharing Code saves time
• Sharing Code eliminate bugs – NOT!
9
Universal Applications – Sharing is Fun (Sometimes)
11
public void OpenLecture(Lecture lecture){
#if WINDOWS_APP_navigationService.Navigate<LectureInfoPage>(lecture);
#else_navigationService.Navigate<LecturePage>(lecture);
#endif}
}
Dependency Inversion Principle
1. “High level modules should not depend on low level modules. Both should depend on abstractions”
2. “Abstractions should not depend on details. Details should depend on abstractions”
( The Dependency Inversion Principle, Robert C. Martin, C++ Report, May 1996 )
13
All the dependency resolving methods we discussed breaks DiP
Redesigning with DiP
15
MainPageViewModel
LectureManager
MdevconService
High
Low
IMdevconService
ILectureManager
Dependency Injection and Inversion Of Control
• Instead of Creating or Resolving my dependencies myselfInject me those dependencies from outside.
• Three primary techniques
– Construction injection
– Property injection
– Parameter injection
16
Redesigning to DI
• Before:
17
public LectureManager(IMdevconService mdevconService){
_mdevconService = mdevconService;}
public LectureManager(){
_mdevconService = new MdevconService.MdevconService();}
• After:
Dependency injection/IoC Container
• A class that is responsible to instantiate other classes
• Provide dependencies to created instances
• Manage Instances Lifetime
• Think of it as a big Dictionary from type to instance(s)
18
Configuring the Container
• Nuget: Install-Package Autofac
• Register Types
20
// Create the builder with which components/services are registered.var builder = new ContainerBuilder();
// Register types that expose interfaces...builder.RegisterType<ConsoleLogger>.As<ILogger>();
// Build the container to finalize registrations// and prepare for object resolution.var container = builder.Build();
Platform Specific Code
• Open a File
22
FileOpenPicker openPicker = new FileOpenPicker();openPicker.ViewMode = PickerViewMode.Thumbnail;openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;openPicker.FileTypeFilter.Add(".jpg");await openPicker.PickSingleFileAsync();
Platform Specific Code – WP8.1
• Open a File
23
FileOpenPicker openPicker = new FileOpenPicker();openPicker.ViewMode = PickerViewMode.Thumbnail;openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;openPicker.FileTypeFilter.Add(".jpg");
openPicker.PickSingleFileAndContinue();
Platform Specific Classes
24
builder.RegisterType<WinFilePickerService>().As<IFilePickerService>().SingleInstance();
builder.RegisterType<WPFilePickerService>().As<IFilePickerService>().SingleInstance();
Lifetime Scope
• Per Dependency\Transient
• Single instance
25
builder.RegisterType<ConcreteClass>();
// ...is the same as this:builder.RegisterType<ConcreteClass>().InstancePerDependency();
builder.RegisterType<ConcreteClass>().SingleInstance();
Lifetime Scope – Per LifetimeScope
26
builder.RegisterType<ConcreteClass>().InstancePerLifetimeScope();
using(var scope1 = container.BeginLifetimeScope()){ var first = scope1.Resolve<ConcreteClass>();
// same instance as firstvar second = scope2.Resolve<ConcreteClass>();//Same as first
}
Registering Types
27
builder.RegisterType<DebugLogger>.As<ILogger>();
builder.RegisterInstance(new NavigationService(rootFrame)).As<INavigationService>();
builder.RegisterType<FirstViewModel>().AsSelf();
builder.RegisterType<FirstViewModel>().AsSelf();builder.RegisterType<SecondViewModel>().AsSelf();builder.RegisterType<ThirdViewModel>().AsSelf();::builder.RegisterType<LastViewModel>().AsSelf();
Registering Types – Assembly Scanning
28
builder.RegisterAssemblyTypes(typeof(MainPageViewModel).GetTypeInfo().Assembly).Where(t => t.Name.EndsWith("ViewModel")).AsSelf();
builder.RegisterAssemblyTypes(typeof(ClassInLibrary).GetTypeInfo().Assembly).Except<MyUnwantedType>().AsImplementedInterfaces();
Modules
• Separation on Concerns
29
class CommonClassesModule:Module{
protected override void Load(ContainerBuilder builder){
base.Load(builder);builder.RegisterType<LectureManager>().As<ILectureManager>().SingleInstance();builder.RegisterType<DummyMdevconService>().As<IMdevconService>().SingleInstance();
}}
builder.RegisterModule<CommonClassesModule>();
builder.RegisterAssemblyModules(assembly);
DI Traps – Cyclic Dependencies
30
class ErrorReporter:IErrorReporter{
public ErrorReporter(IMailClient mailer){
}}
class MailClient : IMailClient{
public MailClient(IErrorReporter reporter){
}}
Cyclic Dependencies – Using Lazy
31
class ErrorReporter:IErrorReporter{
public ErrorReporter(
Lazy<IMailClient> lazyMailer){
_lazyMailer=lazyMailer}
}
class MailClient : IMailClient{
public MailClient(Lazy<IErrorReporter> lazyReporter)
{_lazyReporter=lazyReporter;
}}
_lazyMailer.Value.Send(…); _lazyReporter.Value.Report(…);
Cyclic Dependencies – Property Injection
32
class MailClient : IMailClient{
public MailClient(){}
public IErrorReporter ErrorReporter{
get; set;}
}
class ErrorReporter:IErrorReporter{
public ErrorReporter(){}
public IMailClient MailClient{
get; set;}
}
builder.RegisterType<MailClient>().As<IMailClient>().SingleInstance ().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
Summary
• Follow the OODP
• There is no replacement for good Architecture
• DI is not a Silver Bullet
• Maintainability
• Speed
• Thank You!
33
Presenter contact details
t: @tamir_dresher
b: TamirDresher.com
w: www.codevalue.net