Cross post from IRefactor
Visual Studio can be a swiss knife in the hands of a Software Developer.
You want a fully functional application within several hours; here you go sir:
- Create Windows Forms Application.
- Add relevant Data Sources (in Figure 1 below:
Courses
table). - Drag generated entities from the Data Sources onto relevant Forms.
- Add minimum code to show the Forms in the required order.
- Compile & execute.
Walla, I present you an “enterprise application”!
Though it looks amateur, it allows fully functional CRUD operations.
Figure 1
But, the power can corrupt…
Lately, I came across a few projects implemented using the above approach. Needless to say, the projects were “spaghetti code” - all the UI elements, visual states, business logic & data access, were centralized within a few Forms.
(Don't think it's a rare situation! There are enough companies that try to "save" time using such a “quick & dirty” approach).
I am a pragmatic Software Engineer and I do believe that the “quick & dirty” approach is vital in some cases.
If you present a demo application that demonstrates your capabilities; go ahead, use the necessary tools to implement it as fast as possible.
However, if you develop a real life application using the above approach, it’s a sure road to hell. Slowly but surely, the “fast development” takes more and more time:
- The customer’s button event click is very similar to the employee’s event click, but correlate and update different UI elements » you duplicate the logic in both places.
- New requirement add new conditions to some wizard flow » you add an “
if
” statement to address the conditions. Sometimes, you add a “switch
” statement due to the fact that many “if
s” already exist. - New requirements impose saving more data » you add more parameters to your methods. You also go further and add those parameters to each and every place (form/method) where the change is required.
- The classes and methods become huge, they are overcrowded with UI elements, visual states transformations and business logic. Next time you try to read the code, you will need at least a few hours to concentrate and walk through the mess.
The Project Manager responsible for the aforementioned projects, said to me:
“Any change request I want to apply, come back with unrealistic time estimations. When I ask the developer why it takes so long, he answers: My Gosh – do you know what it takes to change that code? Do you understand how long I need to retest the application, just to verify I didn’t break anything?”
It seems obvious that if in the first place, the project had been written in clear separation of UI, Business Logic and Data Access Layers, it would have been easier to maintain the product lifetime. However, clearly it is not the case here, so what can we do?
One of the tools we can apply here is: Refactoring.
Refactoring is a process of improving software’s internal structure without changing its external behavior. Refactoring ensures completeness of the external behavior, by executing automatic unit tests. We improve the code in incremental small steps (refactoring steps) and each change is followed by a compilation and unit tests execution.
In the following posts, I will demonstrate refactoring called “Separate Domain from Presentation”, which obviously breaks the tight coupling between UI and BL. After finishing the demonstrations of the refactoring process itself (it will take me several posts to do it), I will also explain how to create the suite of unit tests, which allow validation of external behavior completeness.
Remarks
Refactoring Steps
- When the
Irefactor.CoursesView
project was defined initially, Visual Studio generated a DataSet
called CoursesDS
for the Courses Data Source. As you can see, the Irefactor.CoursesView
project mixes UI (FrmMain
) elements and Domain (CoursesDS
) elements.
Figure 2
- Let’s start by removing the
CoursesDS
domain object from the UI (View
).
Create a project IRefactor.Common
and copy there CoursesDS DataSet
.
(Drag the CoursesDS
from project to project, Visual Studio will do the rest).
Due to the fact that CoursesDS
is a VS auto generated class, one should also copy the connection string definition which is stored in the Settings of the IRefactor.CoursesView
project. Add Settings.settings
to IRefactor.Common Properties folder and copy the connection string.
Figure 3
- Compile the Solution and execute the Unit Tests.
- Change the
FrmMain
(View
) to use the newly created IRefactor.Common.CoursesDS DataSet
.
Open the FrmMain
’s designer. Remove the coursesDS
(the instance of the domain object: IRefactor.CoursesView.CoursesDS DataSet
– see red arrow 1).
Figure 4
- On the Toolbox window, under the recently added
IRefactor.Common
Components, you will see the IRefactor.Common.CoursesDS DataSet
.
Drag this CoursesDS
to the FrmMain Form
.
Figure 5
- Dragging the
IRefactor.Common.CoursesDS DataSet
onto the FrmMain
surface generates an instance of the class. Rename it to coursesDS
(to match the previous name). - Repeat the three last steps also for
IRefactor.CoursesView.CoursesTableAdapter
(see Figure 4, red arrow 2) - Select the
BindingSource
(see Figure 4, bsCourses
) and change its DataSource
to coursesDS
instance (coursesDS
now comes from IRefactor.Common
project). Also change its DataMember
to point the Courses
table.
Figure 6
- In
FrmMain
code behind file, add reference to the IRefactor.Common namespace
and change the CoursesDS
to a fully qualified name.
using IRefactor.Common;
private void coursesBindingNavigatorSaveItem_Click
{
(object sender, EventArgs e)
IRefactor.Common.CoursesDS.CoursesDataTable changes =
this.coursesDS.Courses.GetChanges()
as IRefactor.Common.CoursesDS.CoursesDataTable;
}
- Compile the Solution and execute the Unit Tests.
- Go over
IRefactor.CoursesView
and remove all instances of the previous domain object IRefactor.CoursesView.CoursesDS DataSet
. Also, delete the CoursesDS.xsd file from the project. - Compile the Solution and execute the Unit Tests.
We have successfully completed the first steps. We have separated the UI project called IRefactor.CoursesView
from the Domain Project called IRefactor.Common
. Now it’s time to become serious and continue the refactoring towards the MVP pattern.
Codeproject