Reputation: 14935
Is there any difference between Generic Classes and Dependency injection ? Are they not ways to implement Inversion of Control
Is generic class not a way to implement Dependency Injection with added benefits of compile time safety ?
For Example, if I have a node class, then I can define as following
class Node<T> where T : ISomeInterface
{
..
..
}
class Node
{
ISomeInterface obj
public Node(ISomeInterface inject)
{
obj = inject;
}
}
UPDATE 2 With New
class Node<T> where T : ISomeInterface, new()
{
ISomeInterface obj
public Node()
{
obj = new T();
}
}
Update 3 @akim : I have made the example that you asked for using Generics Repository using Generics
Interface IRepository
{
public DataTable GetAll();
}
public class ProductRep : IRepository
{
public DataTable GetAll()
{
//implementation
}
}
public class MockProductRep : IRepository
{
public DataTable GetAll()
{
//mock implementation
}
}
public class Product<T> where T : IRepository, new()
{
IRepository repository = null
public Product()
{
repository = new T();
}
public List<Product> GetProduct()
{
DataTable prodlst = repository.GetAll();
//convert to List of products now
}
}
//so while using the Product class, client would Supply ProductRep class and in NUnit you //would supply MockProductRep class
Product<ProductRep> obj = new ProductRep<ProductRep>();
List<Product> lst = obj.GetProduct();
//in NUnit
Product<MockProductRep> obj = new ProductRep<MockProductRep>();
List<Product> lst = obj.GetProduct();
Upvotes: 8
Views: 5930
Reputation: 8679
Generics introduce the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by code msdn. And generics with all their restrictions and check are applied during compile time using static analysis.
In other hand, Dependency injection is a software design pattern that allows a choice of component to be made at run-time rather than compile time wiki. And object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis wiki.
Answer on your question: one applied at compile time using static analysis, another applied at run time using XML or in-code configuration (it should be also valid for compile). Using Dependency injection decision about binding will be postponed until more information or configuration will be available from the context. So generics and dependency injection are different, and used for different purpose.
Sample #3 answer
Let's move one step further and provide Repository<Entity>
to Controller
and think about it usage. How are you going to implement controler's constructor:
public ControlFreakController<Repository<Entity>>()
{
this.repository = new Repository<Entity>(); // here is a logical problem
}
or
public ControllerWithInjection(IRepository repository)
{
this.repository = repository;
}
And how will you cover ControlFreakController
with tests, if it depends on Repository<Entity>
(literally hardcoded)? What if Repository<Entity>
has no default constructor, and has its own dependencies and life time (for example, there should be one and only one repository rep HTTP request)? What if next day it will be required to audit work with Repository<Entity>
?
Upvotes: 3
Reputation: 4812
They are not the same. Generic types allow you to define functionality that can be applied to a wide range of other types. However when you instantiate a generic class, the compiler makes a reference to the actual types that were passed as generic parameters. So the declaration is static and cannot change after compilation. For example, I can write code that instantiates your Node class:
Node<SomeImplementation> node1 = new Node<SomeImplementation>();
Node<SomeOtherImplementation> node2 = new Node<SomeOtherImplementation>();
I am reusing your Node class in different scenarios, but once I have compiled my assembly, I cannot change the generic type of my variables (node1 and node2).
Dependency Injection (and IoC containers), on the other hand, allow you to change the functionality of your app at runtime. You can use Dependency Injection to swap out one implementation of ISomeInterface
with a totally different implementation at runtime. For example, in your second node class, I can use an IoC container to create the Node class... something like:
Node n = Container.Create<Node>();
The IoC container then figures out how to instantiate the Node class based on some configuration. It determines that the constructor needs an implementation of ISomeInterface
, and it knows how to build an implementation at runtime. I can change my configuration for the IoC container and execute the same assembly/code and a different implementation of ISomeInterface
will be created and passed to the constructor of Node.
This is useful in unit tests, because you can mock out certain parts of your application so that one specific class can be tested. For example, you may want to test some business logic that usually accesses a database. In your unit test, you can mock your data access logic and inject new functionality that returns 'static' data that is needed to test each particular business case. This breaks your tests dependency on the database and allows for more accurate/maintainable testing.
Edit
With regards to your update, the parameter-less constructor restriction may not always be desired. You may have a class (written by you or a third party) that requires parameters. Requiring a class to implement a parameter-less constructor may effect the integrity of the application. The idea behind the DI pattern is that your Node class doesn't need to know how the class was actually created.
Suppose you had many layers of classes/dependencies. With generic types, it might look like this:
class MyClass<T>
where T : IUtilityClass
{
...
}
class UtilityClass<T> : IUtilityClass
where T : IAnotherUtilityClass
{
...
}
class AnotherUtilityClass : IAnotherUtilityClass
{
...
}
In this case, MyClass uses UtilityClass, and UtilityClass depends on AnotherUtilityClass. So when you declare MyClass
, you must know every dependency down the line... not just the dependencies of MyClass
, but also the dependencies of UtilityClass
. This declaration looks something like this:
MyClass<UtilityClass<AnotherUtilityClass>> myTestClass =
new MyClass<UtilityClass<AnotherUtilityClass>>();
This would get cumbersome as you add more and more dependencies. With DI, your caller doesn't need to know about any of the nested dependencies because the IoC container automatically figures them out. You just do something like this:
MyClass myTestClass = Container.Create<MyClass>();
There's no need to know anything about the details of MyClass or it's utility classes.
There are usually other benefits to IoC containers as well, for example many of them provide forms of Aspect Oriented Programming. They also allow you to specify the lifetime of an object, so an object could be a singleton (only one instance will be created, and the same instance will be returned to all callers).
Upvotes: 3
Reputation: 65087
I'm going to assume you mean your generic class to look like this:
class Node<T> where T : ISomeInterface {
T obj;
public Node(T inject) {
obj = inject;
}
}
..in which case, you're just opening up a generic type for dependency injection (with a restraint). You haven't discovered a different "method" of dependency injection - it is still dependency injection.
This wouldn't be very useful in a "real-world" scenario. You've made assumptions on how the type parameter would be used purely based on injecting it and restraining it. Also, you'll only ever be able to inject 1 single type of object into this, which is a very bad assumption.
After your update using new(), you've got even more issues. Your injected type must allow parameterless construction. That limits you even further.
Upvotes: 0