Going towards Inversion of Control and better design Omar AL Zabir
Typical code  public class CustomerManager { public void Insert(Customer customer) { CustomerData data = new CustomerData(...
Visualize the design
Problems? <ul><li>CustomerManager  has hard dependency on  CustomerData ,  Logger , and  Mailer . </li></ul><ul><li>If we ...
Solution <ul><li>Make  CustomerManager  completely independent of any other class. </li></ul>class CustomerManager { priva...
Design is much better, less coupling
Principles <ul><li>If  ClassA  needs  ClassB , instead of doing  new ClassB , take an interface in the constructor and use...
Pains <ul><li>It’s a pain to create  CustomerManager . Everytime you have to pass all the dependencies. </li></ul><ul><li>...
Solution <ul><li>Use a default constructor </li></ul>public class CustomerManager { private ICustomerData _customerData; p...
Wrong! <ul><li>You introduced hard dependency again. </li></ul><ul><li>Solution is to use a Container. </li></ul>
What’s a Container <ul><li>Container is like a registry of interfaces and their implementation. </li></ul><ul><li>It store...
Registering in container <ul><li>In your  Global.asax  or Main function, you initialize the Container </li></ul>Global.asa...
Using Container <ul><li>The default constructor of CustomerManager resolves the implementation of the interfaces from the ...
Benefits? <ul><li>You can change the current implementation of an interface in one place and the whole project gets it.  <...
What problems does this solve? <ul><li>It makes your code unit testable. </li></ul><ul><li>By doing this, you make your cl...
Common reactions to this <ul><li>I have a lot of code in my project. I can’t make such drastic changes now and miss delive...
More common reactions <ul><li>Isn’t it slower than just doing “ new SomeClass ”? </li></ul><ul><ul><li>Negligible. You won...
How do you know you are doing it right? <ul><li>You like to implement Container, do Dependency Injection, achieve “Inversi...
Upcoming SlideShare
Loading in …5
×

Going towards inversion of control and better design

1,211 views
1,134 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,211
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
20
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Going towards inversion of control and better design

  1. 1. Going towards Inversion of Control and better design Omar AL Zabir
  2. 2. Typical code public class CustomerManager { public void Insert(Customer customer) { CustomerData data = new CustomerData(); var newCustomer = data.Insert(customer); Logger.Log(&quot;New customer inserted&quot; + newCustomer.Name); Mailer.SendWelcomeMail(newCustomer); return newCustomer; } } Here’s a typical example of code we usually do: A business layer class A data access layer class Utility class
  3. 3. Visualize the design
  4. 4. Problems? <ul><li>CustomerManager has hard dependency on CustomerData , Logger , and Mailer . </li></ul><ul><li>If we decide to change the Logger class to some other class, we will have to go throw all such Manager classes and do search replace. </li></ul><ul><li>We cannot unit test CustomerManager unless CustomerData properly works with database, Logger can write log to disk, Mailer has a SMTP server to send mails to. </li></ul>
  5. 5. Solution <ul><li>Make CustomerManager completely independent of any other class. </li></ul>class CustomerManager { private ICustomerData _customerData; private ILogger _logger; private IMailer _mailer; public CustomerManager( ICustomerData customerData, ILogger logger, IMailer mailer) { _customerData = customerData; _logger = logger; _mailer = mailer; } public void Insert(Customer customer) { _customerData.Insert(cusotmer); _logger.Log(&quot;New customer inserted&quot;); _mailer.SendWelcomeMail(customer); } }
  6. 6. Design is much better, less coupling
  7. 7. Principles <ul><li>If ClassA needs ClassB , instead of doing new ClassB , take an interface in the constructor and use that. </li></ul><ul><li>Never do new Something() . As soon as you do it, you introduce a hard dependency. </li></ul><ul><li>Never call static methods on your classes Logger.Log . You introduce a hard dependency. </li></ul>
  8. 8. Pains <ul><li>It’s a pain to create CustomerManager . Everytime you have to pass all the dependencies. </li></ul><ul><li>Higher level needs to know what are the lower level classes. For ex, say in default.aspx : </li></ul>CustomerManager manager = new CustomerManager( new CustomerData(), new Logger(), new Mailer());
  9. 9. Solution <ul><li>Use a default constructor </li></ul>public class CustomerManager { private ICustomerData _customerData; private ILogger _logger; private IMailer _mailer; public CustomerManager() : this(new CustomerData(), new Logger(), new Mailer()) { } public CustomerManager(ICustomerData customerData, ILogger logger, IMailer mailer) {
  10. 10. Wrong! <ul><li>You introduced hard dependency again. </li></ul><ul><li>Solution is to use a Container. </li></ul>
  11. 11. What’s a Container <ul><li>Container is like a registry of interfaces and their implementation. </li></ul><ul><li>It stores which class is the current implementation of which interface. </li></ul><ul><li>Containers – Microsoft Enterprise Library Unity, Ninject, Munq. </li></ul>
  12. 12. Registering in container <ul><li>In your Global.asax or Main function, you initialize the Container </li></ul>Global.asax Application_Start() { Container.Register< ILogger , Logger2 >(); Container.Register< ICustomerData , CustomerDataLinqToSql >(); }
  13. 13. Using Container <ul><li>The default constructor of CustomerManager resolves the implementation of the interfaces from the Container. </li></ul>public class CustomerManager { private ICustomerData _customerData; private ILogger _logger; private IMailer _mailer; public CustomerManager() : this( Container.Resolve<ICustomerData>(), Container.Resolve<ILogger>(), Container.Resolve<IMailer>() ) { } public CustomerManager(ICustomerData customerData, ILogger logger, IMailer mailer) {
  14. 14. Benefits? <ul><li>You can change the current implementation of an interface in one place and the whole project gets it. </li></ul><ul><li>Say, you change the registration of ILogger to: </li></ul><ul><ul><li>Customer.Register<ILogger, LoggerUsingDatabase>() </li></ul></ul><ul><li>Everyone uses logging on database. </li></ul>
  15. 15. What problems does this solve? <ul><li>It makes your code unit testable. </li></ul><ul><li>By doing this, you make your classes independent of each other. CustomerManager does not know who inserts record in database, who logs it, who mails it. This is good. </li></ul><ul><li>When you have a defective class, you can easily take it out and replace it with a correct implementation by changing the Registration at Container. </li></ul><ul><li>It makes refactoring much simpler. There’s very little coupling between classes. So, we can easily refactor code as long as the interfaces don’t change. </li></ul><ul><li>We can disable certain class by replacing the implementation of an interface with a stub. For ex, if we want to disable Logging, we will register a stub for ILogger. </li></ul>
  16. 16. Common reactions to this <ul><li>I have a lot of code in my project. I can’t make such drastic changes now and miss deliveries! </li></ul><ul><ul><li>Use Visual Studio. Right click on a class and select Generate Interface. It will generate an interface for you. Then where you have done “new ClassA”, you replace it with “Container.Resolve<IClassA>()”. Voila! </li></ul></ul><ul><li>I don’t see how containers work. How does it get the right class? </li></ul><ul><ul><li>Container is like a dictionary. It remembers for which interface, which class it needs to create. When you call Container.Resolve<ISomeInterface>(), it looks at the class that was registered for this interface, say Container.Register<ISomeInterface, SomeClass>(); </li></ul></ul>
  17. 17. More common reactions <ul><li>Isn’t it slower than just doing “ new SomeClass ”? </li></ul><ul><ul><li>Negligible. You won’t notice it unless you are doing a million “ new Someclass()” in a row. </li></ul></ul><ul><li>But my class takes constructor arguments. How does Container provide the right arguments? </li></ul><ul><ul><li>You can do: Container.Resolve<ICustomerData>(arg1, arg2, arg3); </li></ul></ul><ul><li>I still don’t get it. Aren’t we just changing the use of class to use of interface at the cost of added complexity? </li></ul><ul><ul><li>If you are asked to change the class that does email and replace it with another class, can you do it in less than 5 lines of code change throughout your project? </li></ul></ul><ul><ul><li>Can you test some class in business layer while database connection is unavailable without doing more than 5 lines of code change? </li></ul></ul>
  18. 18. How do you know you are doing it right? <ul><li>You like to implement Container, do Dependency Injection, achieve “Inversion of Control” in your project. How do you know you are doing it right? </li></ul><ul><ul><li>There’s no “new SomeClass(...)” in your code anymore. </li></ul></ul><ul><ul><li>You can mock lower layer classes and test a high layer class in complete isolation. For ex, test CustomerManager without having database connection available. </li></ul></ul>

×