Separate Domain from Presentation – Part III
Third post in the series of posts about “Separate Domain from Presentation”
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 accessCoursesView
directly. After all, the only functionality of interest to theCoursesPresenter
is theCoursesView Courses
property. Therefore, we will extract aninterface
fromCoursesView
class as follows: right click on theCoursesView
class » Refactor » Extract Interface and selectCourses
property as shown in the figure below: - Compile the Solution and execute the Unit Tests.
- In the
CoursesPresenter
class, change all the occurrences ofCoursesView
toICoursesView
. - 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 theinterface
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 theLoad
andSave
methods toLoadCoursesEventHandler
andSaveCoursesEventHandler
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
andCoursesView.Save
methods to callNotifyObservers
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 theLoad
andSave
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
andLoad
methods further more, from the presenter to the DAL. - Create the
CoursesView
andCoursesPresenter
using Abstract Factory or using Dependency Injection.