Design patterns in real world use case scenarios – Dependency injection (set type)
To get a detail explanation of what this pattern stands for check out the Dependency injection post where I posted detail article about dependency injection pattern
How injection can be a pleasant thing
A good development practice is to encapsulate page functionality in a set of controls, because it is easier to maintain that code, performances can be tuned on more granular level, multiple developers can work on the functionality of the same page, we can reuse the control on some other page with similar UI. (I'll stop here writing ode to web controls - I'm sure you read this in almost every ASP .NET book you've seen)
But in real world this composite UI implementation usually comes with a cost due to several reasons:
a) Strict control encapsulation creates redundant code among controls and multiplies calls to database because every control has to retrieve it's own piece of data, update it's own piece of data etc. (Btw, this can be handled with a little async programing, but we today speak about cleaner way)
b) To solve redundancy in a) developers pretty often put shared logic on a page level and then do some "blind casting" like
((SomeSpecificPage)this.Page).SomeSharedElement
This is also a bad practice because sooner or later we would want to use this control on another page and then that require "spaghetti" coding such as"
if (this.Page as SomeSpecificPage!=null)
...
elseif (this.Page as SomeSpecificPage2!=null)
This burden can be eased by pages implementing the same interface, but sooner or later we would like to use a control on page X which is not implementing that interface and creating interfaces everywhere so the control could work doesn't sound clean to me.
Also, to me the fact that control is aware of it's surroundings looks as a bad OOP practice too
c) To solve the hard coupling done in c), developers usually strive to event publishing, observer pattern, registry pattern etc. Problem from b) is solved by that but we also got a page for which maintenance we need a "map".
The solution here which would be presented tries to solve all 3 problems (redundant db calls, control coupled to its surroundings, high complexity) on a very simple and pleasant way - by using dependency injection setter type, where the page would inject itself to controls on highly simple and decoupled way
Problem - use case
Make a web page which would be used by user to update their data.
User data are: last name, first name, street, city, yahoo, email.
Page should be made of 3 different controls: one for names, one for address and one for Internet attributes. Desired functionality is that controls should encapsulate and separate concerns between them on class level as they would do on UI level without additional overhead caused
Why dependency injection pattern?
Dependency injection pattern in short is a pattern where some type is having a property and implementing its own functionality referring to it, but doesn't set that property by himself. Instead, some external type sets the property value either through the type constructor or by using property setter.
So all you need is a class with a property and: either constructor setting the field property wraps or a property has to implement set block.
If we would take a look at our problem we would see that we need that someone somehow inject into the control user data required by control without control knowing who, how and when. Usage of web page constructors is unusual in web development so we would pick the solution of using property set block - dependency injection setter type
Rule of thumb
In a way, DI can be treated as a "Stranger strategy" because that is a strategy variation where the behavior is defined outside of the type implementing strategy
Usage of dependency injection pattern is usually appropriate when we have a situation where one object for its functionality needs information about the state of another object but we don't want to have any kind of coupled relation between the two objects (such as inheritance, composition).
Sound like our case!
Problem - solution example
Class Diagram of the solution
Page level code
Our page would contain those three controls (ControlA, ControlB and ControlC) which are supposed to handle their own part of editing user data
The page would also have a "SaveUser" button which clicking should persist the updates done by page user
User service
Let's imagine that page for loading user data requires to call a UserService component which makes a very expensive call to database (web service etc) and retrieves whole set of user data.
Something like this diagram
and this code
public class UserService
{
public static User GetUserFromDatabase()
{
// let's imagine there's been a very expensive database call
//which retreive user data
return
new User("Prague", "malovicn at vuscode dot com", "Nikola",
"Malovic", "Bryksova", "nmalovic_monster at yahoo dot com");
}
}
Allowing every control to repeat this call to retrieve their subset of data is not acceptable so we would make that call only once from page level and store the result on page level in a UserData property.
So the class diagram of the page would look like this:
(I'll touch later initControl method)
UserAwareControl
Even I like more interfaces, in this use case usage of abstract classes is more appropriate, because there is a common functionality (not only behavior) of the user controls. They all need user data so they could present them.
So the base control class would look like this:
public abstract class UserAwareControl : UserControl
{
private User _userData;
public User UserData
{
set
{
_userData = value;
UpdateUIControls();
}
get { return _userData; }
}
public abstract void UpdateUIControls();
}
There is a property UserData of type User which setter would be used for injection and there is an abstract method UpdateUIControls() which all controls have to implement and which enforce the update of controls when the page would set control user data
Control code
Every control would follow the same pattern as the ControlA does. Only difference would be then that the ControlB would have Street and City text boxes and access appropriate properties; ControlC Email and IM text boxes with accessing appropriate user data properties;
They all look like this:
public partial class ControlA : UserAwareControl
{
protected void Page_Load(object sender, EventArgs e)
{
UserData.FirstName = FirstName.Text;
UserData.LastName = LastName.Text;
}
public override void UpdateUIControls()
{
LastName.Text = UserData.LastName;
FirstName.Text = UserData.FirstName;
}
}
As you can see, control overrides abstract method and takes the property value and sets the ui elements with it.
On a page load event, control takes the content of the text boxes and sets the base control property value.
The injection
The injection would occur on a page level, during the Page_Init event. (Page level because page knows about controls; Page_Init - we need it just once). Page would first made expensive call to retrieve user data and then it would recurevly iterate through the control collections checking if the control is of UserAwareControl type. For all the controls found UserData property value would be set to reference to page property containing user data and by that page would by that inject itself to control
The same thing said through the code could look like this
private User PageUser;
protected void Page_Init(object sender, EventArgs e)
{
// we load this only once
PageUser = UserService.GetUserFromDatabase();
initControls(Controls);
}
private void initControls(ControlCollection controls)
{
foreach (Control control in controls)
{
/// a little bit of composite recursion
if (control.Controls.Count > 0)
initControls(control.Controls);
UserAwareControl userAwareControl = control as UserAwareControl;
if (userAwareControl != null)
{
// setting the control to be aware of page user
userAwareControl.UserData = PageUser;
}
}
}
So, when some control sets a UserData property of the base control, the PageUser property is been set by that, because what we inject to control property is the reference to the page property.
That's how control gets the page single loaded data without coupling itself to page and without complex wire ups between them!
Testing the solution
When we start the web page we see that the control ui elements have been populated
After that I have updated some of the text boxes values
and then I clicked the button and the result showed that the code is working properly
Problem solved!
To download source code click here: Example source code