Saturday, May 30, 2009

Separate Domain from Presentation – part III

Cross post from IRefactor

This is a third post in the series of posts about “Separate Domain from Presentation” Refactoring.

Previous Posts:
Separate Domain from Presentation – part I
Separate Domain from Presentation – part II

Last time we explained how to refactor towards MVP – Supervising Controller pattern.
We left our project in the following state:
In this post I will complete the required refactoring steps and will suggest more steps to even deepen the separation of UI and BL concerns.

Refactoring Steps:
  • "Extract Interface" – in order to have a better encapsulation and separation of concerns, the CoursesPresenter shouldn’t access CoursesView directly. After all, the only functionality of interest to the CoursesPresenter is the CoursesView Courses property. Therefore, we will extract an interface from CoursesView class as follows: right click on the CoursesView class » Refactor » Extract Interface and select Courses property as shown in the figure below.
  • Compile the Solution and execute the Unit Tests.
  • In the CoursesPresenter class change all the occurrences of CoursesView to ICoursesView.
  • Compile the Solution and execute the Unit Tests.
  • Last time we indicated that the presenter should handle complicated user events by subscribing to the view. After introducing the ICoursesView interface it’s simple. Add to the interface the following code:
event Action LoadCourses;
event Action SaveCourses;
  • Implement the newly added events in the CoursesView class:
public event Action LoadCourses;
public event Action SaveCourses;
  • In the CoursesPresenter class rename the Load and Save methods to LoadCoursesEventHandler and SaveCoursesEventHandler respectively. Use right click » Refactor » Rename tool to rename it easily.
  • Wire-up the events in the CoursesPresenter constructor as follows:
public CoursesPresenter(ICoursesView view)
{
this.view = view;
view.LoadCourses += LoadCoursesEventHandler;
view.SaveCourses += SaveCoursesEventHandler;
}

  • Compile the Solution and execute the Unit Tests.
  • In the CoursesView class add the notification code:
private void NotifyObservers(Delegate del)
{
Delegate[] observers = del.GetInvocationList();
foreach (Delegate observer in observers)
{
try
{
Action action = observer as Action;
if (action != null)
{
action.DynamicInvoke();
}
}
catch
{
// graceful degradation.
}
}
}
  • Change the CoursesView.Load and CoursesView.Save methods to call NotifyObservers respectively:
private void FrmMain_Load(object sender, EventArgs e)
{
//...
NotifyObservers(LoadCourses);
//...
}
private void Save()
{
//...
NotifyObservers(SaveCourses);
//...
}
  • Compile the Solution and execute the Unit Tests.
  • Now it is the time to remove all the temporary instantiations of the CoursesPresenter class in the Load and Save methods. Remove all the occurrences.
  • In the Program.cs class instead of Application.Run(new CoursesView()) write the following:
static void Main()
{
//...
CoursesView coursesView = new CoursesView();
CoursesPresenter coursesPresenter = new CoursesPresenter(coursesView);
Application.Run(coursesView);
}


This concludes the “Separate Domain from Presentation” refactoring.
We ended with the following:
For next possible steps, consider the following:
  • Go over the CoursesView.Designer.cs and remove all the TableAdapter instances.
  • Create DAL and move Save and Load methods further more, from the presenter to the DAL.
  • Create the CoursesView and CoursesPresenter using Abstract Factory or using Dependency Injection.

No comments:

Post a Comment