Cross post from IRefactor
This is the third post in the series of posts about “Separate Domain from Presentation” Refactoring.
Previous Posts
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
{
}
}
}
- 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.
Codeproject