If you are in the business of programming for some time, you likely know “the faster, the better” when it comes to the performance of the programming.
Take an example, in Microsoft store app certification requirement, it stated that app should response to any user action within 2 seconds in X86 or X64 and within 5 seconds in ARM architecture. and Microsoft has a certification verification kit to verify your app. Any app failed in this requirement would not get to the store.
You may also know that “The less is better” when it comes to line of code of the programming. if you can get the job done in 100 line, you would not want to make it to 110. The statistical study indicates that the more the lines of code, the more bugs it will be and the higher the maintained cost.
In most of cases, we programmers are fighting between these 2 goals. because in most of cases, faster performance means more code, but more code also mean longer execution time… this is an art of balancing.
Take a look at this block of code in CustomerBO:
Public Function GetCustomerDemographicInfo(ByVal customerID As Long, ByVal transactionID As Long, ByVal isTxnSummary As Boolean) As CustomerDemographicBE
Dim custDemographic As CustomerDemographicBE = Nothing
Dim customerDAO As CustomerDAO
customerDAO = New CustomerDAO
custDemographic = New CustomerDemographicBE
custDemographic = customerDAO.GetCustomerDemographicInfo(customerID, transactionID, isTxnSummary)
Return custDemographic
End Function
CusotmerBO will surely interact with CustomerDAO quite often. this looks like you are doing restaurant business and you hire one chef to cook one dish and fire him then hire another chef to cook another dish… There is lot of effort spent in hiring and firing. ( instantiation and discard). In term of Line Of Code, you will see this line “Dim customerDAO As CustomerDAO = New CustomerDAO” in almost every methods in the class.
As the rule says “ the less the better” and also out of laziness, you want to write less code, right? so you want to instantiate the class once and use it everywhere. so you thought of a perfect place to do this. “Constructor” . OK, you put this line of code in constructor and use it in all methods within the class. The modified code will be similar to the following:
Public Sub New()
mCustomerDAO = New CustomerDAO()
End Sub
and
Public Function GetCustomerDemographicInfo(ByVal customerID As Long, ByVal transactionID As Long, ByVal isTxnSummary As Boolean) As CustomerDemographicBE
Dim custDemographic As CustomerDemographicBE = Nothing
custDemographic = New CustomerDemographicBE
custDemographic = mCustomerDAO.GetCustomerDemographicInfo(customerID, transactionID, isTxnSummary)
Return custDemographic
End Function
If you do that in all methods within a class and do that for all BO classes, your total line of code will be around 5% ( in the size of our code base total size of 800K, it likely mean 40K LOC reduction) and in most of case, your performance will be improved…
well, I made this statement is based on the assumption that you have a very good object model and there is in no time when an object is instantiated but never be used. It turns out that this is a huge assumption to be made. In most of cases it is turns out to be a false assumption. This is especially the case in our codebase. Take a look at this constructor :
Public Sub New()
mSelfAndMedicalCertificationBO = New SelfAndMedicalCertificationBO()
mVehicleBO = New VehiclesBO
mPlacardBO = New DisabilityPlacardBO()
mDrivingLicenseBO = New DrivingLicenseBO()
mTransactionDAO = New TransactionDAO()
mPlateBO = New PlateBO
customerDAO = New CustomerDAO()
mDeficiencyDao = New DeficiencyDAO
'objDrivingLicense = New DrivingLicenseDAO()
mInterfaceBO = New InterfaceBO()
mOrganDonorVoterRegistrationDAO = New OrganDonorVoterRegistrationDAO()
mRegistrationBO = New RegistrationBO
mCustomerBO = New CustomerBO()
mOwnershipBO = New OwnerShipBO
mTitleBO = New TitleBO
mFlashDAO = New FlashDAO
mFlashBO = New FlashBO
mOfficeBO = New OfficeBO
mPlateDAO = New PlateDAO
mPaymentProcessingBO = New PaymentProcessingBO
mRegistrationDAO = New RegistationDAO
mDriverRecordSaleDAO = New DriverRecordSaleDAO
mLienBO = New LienHolderBO
mCustomerIDsCollector = New TwoKeyDictionary(Of String, Long, Long)
mCommonReplicationBO = New CommonReplicationBO
mOutputDocumentLibraryDAO = New OutputDocumentLibraryDAO
End Sub
You might guess it right, it is for TransactionBO. for these who are familiar with our codebase, you know this class is a “knows all , does all” class. it handles all transactions of all types, from driver to vehicle, from living customer to deceased customer. from BAM transaction to legacy transaction; from interface to interactive… put it in this way “ if you take this class out of the system, NOTHING will work.” You can tell how big it is by looking at the LOC of this class, 16.7K! It is as big as the size of a medium size system. it will never be a bad idea to break this class to smaller ones. For example, make one class handle Title transactions, and make another class handle DrivingLicense transactions . and only keep these common functionality in TransactionBO and make it the base class for all others. Before we do that, we will see lots of objects been instantiated but never be used. ( what a waste! from both performance and line of code point of views).
well, the short term fix could be lazy instantiate. (while I am proposing this temporary fix, I am still calling object structure redesign for TransactionBO, TransactionBPC and TransactionDAO, because the fundamental problems of a huge class handles all still exist and need to be dealt with.)
This is how to make it work.
1)
remove substantiation statements in the construction for these objects may or may not be used in the class. ('mCustomerDAO = New CustomerDAO or Dim 'mCustomerDAO As New CustomerDAO )
2)
for these objects define another private member like this way:
Private _CustomerDAO As CustomerDAO
3)
then change the original mCustomerDAO to make it a read only property
Private ReadOnly Property mCustomerDAO() As CustomerDAO
Get
If _CustomerDAO Is Nothing Then
_CustomerDAO = New CustomerDAO()
End If
Return _CustomerDAO
End Get
End Property
After that, all rest of the code stay as they are. That’s all it takes to implement lazy instantiation.
what is the performance implication ?
Before I recommend this pattern, I tried it with my POC, with this pattern, I managed to reduce response time of my window store app from 15 seconds to less than 2 second when I only implemented it for one of UI class MediaElement. For my case, it is not that I instanced the object without using it. it is rather my strategy to distribute the substantiation time among different clicks. When User click on <lesson> button to go to Lesson page, I hold off the instantiation of the MediaElement object within Lesson page. When user first time click on <Listen> button, I then instantiate the MediaElement and then use the object make speech call.
when my colleague implemented my recommendation in TransactionBPC , the performance test result is shown below:
Methods
(before Fix)
(after Fix)
TransactionBPC.GetOfficeHolidays()
12969 ms
1000 Iterations
3143 ms
1000 Iterations
TransactionBO.GetOfficeHolidays()
2136 ms
1000 Iterations
2177 ms
1000 Iterations
TransactionDAO.GetOfficeHolidays()
1922 ms
1000 Iterations
2108 ms
1000 Iterations
The performance improvement in BPC and BO, DAO is more than 75%. while there is no noticeable change in BO or DAO
The reason I call it Magic Wand are
1) it does not take much effort, nor risk.
2) the pay back is great.
3) it is still a magic like getting a rabbit from a top hat. The real work of growing the rabbit still needed. like this case, to spend time to break the class to smaller ones is still a valid call from architecture point of view.
thanks and hope you enjoy your programming work as I do…