Order Of Inheritance Problem
Today’s programming problem takes a little bit of explaining. I hope you bear with me as there is much to learn about the application of Inheritance to satisfy two of the SOLID Principles.
Consider the following situation. You have been asked to program up a business logic workflow for registering new customers into a system. Since you know how to structure software well, i.e. to accommodate future change requests easily, you recognise that this functionality belongs in a pure business logic workflow module, a use case, and decide to call the class RegisterCustomerUseCase
. RegisterCustomerUseCase
will have a Register()
method taking a CustomerRegistration
model as a parameter. Register()
first calls a helper method, Validate()
, to verify the incoming CustomerRegistration
data. Here is the listing for RegisterCustomerUseCase.Validate()
:
protected virtual async Task Validate(CustomerRegistration registration) { if (registration == null) throw new MissingCustomerRegistration(); registration.Validate(); var existCust = await Repository.GetCustomer(registration.EmailAddress); if (existCust != null) throw new DuplicateCustomerEmailAddress(registration.EmailAddress); }
The detailed validation is not important for the point I am trying to make.
Notice that you can override Validate()
, the virtual
keyword, and that it’s a protected
rather than a private
method.
You’re aware of two further non-functional requirements:
- You want to guard this new customer registrations feature with a feature flag. Once the feature works well and is bedded in, you intend to remove the feature flag.
- You want to be able to log actions that happen in the
Register()
method, especially during the validation.
Since you are a knowledgeable and conscientious developer, you know that the Single Responsibility Principle guides you to separate the pure business logic functionality from the behaviour just for the programmer, i.e. logging and feature flagging. You also know that the Open-Closed Principle prefers us to separate the temporary functionality, the feature flag, from the permanent logging behaviour.
Consequently, you decide use inheriance and create two child classes for RegisterCustomerUseCase
:
FeatureFlaggedRegisterCustomerUseCase
– contains the feature flag functionalityLoggedRegisterCustomerUseCase
– does the logging
Here is the code for the Validate()
method for both these classes:
FeatureFlaggedRegisterCustomerUseCase:
protected override async Task Validate(CustomerRegistration registration) { var isAllowed = await FeatureFlagRepo.IsRegistrationAllowed(registration); if (!isAllowed) throw new NotAllowedToRegisterCustomer(registration); await base.Validate(registration); }
and
LoggedRegisterCustomerUseCase:
protected override async Task Validate(CustomerRegistration registration) { Logger.LogInfo("Start Validation"); await base.Validate(registration); Logger.LogInfo("Finish Validation"); }
Note that the calls to base.Validate()
calls the immediate base class’ Validate()
method.
So, the question is: How should we arrange our inheritance hierarchy?
Here are our options:
Or
Note that the arrow points at the parent class.
Or in other words: Should the FeatureFlagRegisterCustomerUseCase
derive from LoggedRegisterCustomerUseCase
or vice versa?
What do you think? Why do you think so?
Please email me your answers! I am so looking forward to them. Please don’t be shy. You’ll learn more by participating in this exercise.
Also, immortalise your name by furnishing the best answer and get your name into the Code Coach blogs! Even better, I will shout a coffee to the winner!
I’ll reveal the answer tomorrow, including an interesting twist.
Leave a Reply
Want to join the discussion?Feel free to contribute!