the product owner wants to have a filter by subscription as well
What is the motivation at (1) to introduce the ISpecification<T> concept?
There isn't any.
Introducing it at (1) requires prescience that (2) will be required in the future. However time and time again developers have been shown to be terrible at predicting future changes, often increasing complexity to gain generality that never pays off or worse gets in the way of change that is needed later.
The more realistic scenario is that the simple version is implemented at (1) and that ISpecification<T> is introduced at (2) - which means that nothing was ever "closed to modification".
The idea behind this article is that we should write clean code from the beginning and the scenario i described was just to introduce the problem and show that the first code written was not good enough to follow the design principle.
If we talk about the efficiency of the SOLID principles, we have pros and cons but for me the pros are more than cons.
Thanks for your comment.
show that the first code written was not good enough to follow the design principle.
The final code only makes sense when both requirements (1, 2) are known.
Introducing ISpecification<T> when only (1) is known is a guess. It may be an educated guess but the investment doesn't really pay off until (2) materializes - before that the complexity it introduces is non-essential.
Compare that to
Kent Beck
@kentbeck
for each desired change, make the change easy (warning: this may be hard), then make the easy change
23:07 PM - 25 Sep 2012
It acknowledges that earlier we may have had insufficient information to create the optimal design (to grant the freedom to be changed in the way needed) but now that we know what needs to change, we adapt to reflect our new state of knowledge.
Our version 1 "state of knowledge" simply leads to
If we do decide to go ahead with ISpecification<T> because some followup conversations with the stakeholders strongly suggested that there may be other filter types needed later but some code we don't have access to already uses FilterByType we may even end up with
So the real reason to be cautious with the Open-Closed Principle is when it requires you to predict the future - because when you predict the future you'll likely be wrong (YAGNI).
"Be careful when choosing the areas of code that need to be extended; applying the Open-Closed Principle EVERYWHERE is wasteful, unnecessary, and can lead to complex, hard to understand code."
Head First Design Patterns; Eric Freeman & Elisabeth Freeman, 2004; p.87
Robert C. Martin wrote about OCP again in 2014 - essentially advocating a Plugin Architecture. When you design a plugin architecture you have already decided up front along which lines your system needs to be extensible (supported hopefully by good evidence of that necessity/benefit). So OCP is presented as a principle for systems design - not class design.
Also note that ISpecification<T> has exactly one method. As Scott Wlaschin observes function types take SRP and ISP to the extreme.
publicclassCustomerPredicates{publicstaticboolIsGold(Customerc){returnc.Type==CustomerType.Gold;}publicstaticboolIsSilver(Customerc){returnc.Type==CustomerType.Silver;}}publicclassMySpec{privatestaticboolHasMinNameLength(Customerc){returnc.Name.Length>1;}publicstaticboolIsSatisfied(Customerc){returnCustomerPredicates.IsSilver(c)&&HasMinNameLength(c);}}publicclassProgram{publicstaticvoidMain(){varcustomers=newList<Customer>{newCustomer{Name="C1",Type=CustomerType.Silver},newCustomer{Name="C2",Type=CustomerType.Silver},newCustomer{Name="C3",Type=CustomerType.Gold}};/*
Predicate<Customer> hasMinNameLength = c => c.Name.Length > 1;
// Use closure to simulate accessing some other arcane specification
Predicate<Customer> isSatisfied = c => CustomerPredicates.IsSilver(c) && hasMinNameLength(c);
var result = customers.FindAll(isSatisfied);
*/varresult=customers.FindAll(MySpec.IsSatisfied);Console.WriteLine(result.Count);}}
seems like a much less cumbersome way to move forward.
Also i know that applying the Open-Closed Principle EVERYWHERE is wasteful, unnecessary, and can lead to complex, hard to understand code and fully agree with it.
But seriously I’m not a fun of duplicated code and the is a lot of solutions to avoid that !
The idea behind my article was to explain how we can implement the OCP principle inside a software in different ways. This is an article to simplify things to software developers in my point of view.
And in the conclusion a noticed that :
we shouldn't be afraid to do so, it's completely normal, but we should at least make these changes as discrete as possible.
I can’t see what was wrong ?
To conclude : I think, we both agree that SOLID are principles and not rules!!
Thank you for these comments which have raised this thread and i hope that it will be conclusive debate.
DRY isn't about removing repetition or duplication: "every piece of knowledge must have a single, unambiguous, authoritative representation within a system". Sometimes code can look similar but is unrelated.
If the article would have started by stating that it was known up front that bothByType and BySubscription filtering would be needed and that other, yet to be determined kinds of filtering may still need to be added in the future, that would have laid out the background of a clear and necessary dimension of extensibility that OCP could apply to.
As it is, it was presented as if OCP somehow should make it obvious which dimension of extensibility is needed when only ByType filtering is required. Initially there are no actual dimensions of variability indicated. That only happened once the BySubscription requirement surfaced later.
This sets up the unrealistic expectation that IFilter<T> should be introduced at the very beginning (which is revisionist) when in fact the value of that abstraction only becomes clear much later.
In a realistic scenario you would start with FilterByType and end up at Apply - which means that the code isn't "closed to modification" as you are "opening it for extension".
In my example i apply OCP just in time and this what should we do and i Refactor to Open-Closed.
Yagni only applies to capabilities built into the software to support a presumptive feature, it does not apply to effort to make the software easier to modify.
At first reading the open closed principle may seem to be nonsensical. Our languages and our designs do not usually allow new features to be written, compiled, and deployed separately from the rest of the system. We seldom find ourselves in a place where the current system is closed for modification, and yet can be extended with new features.
In OCP, the term module includes all discrete software elements, including methods, classes, subsystems, applications, and so forth. Also, the phrase “closed with respect to X” means that clients are not affected if X changes.
Indeed, most commonly we add new features by making a vast number of changes throughout the body of the existing code. We’ve known this was bad long before Martin Fowler wrote the book[2] that labeled it Shotgun Surgery but we still do it.
[2]Hall, 1988. [2] Refactoring, Martin Fowler, Addison Wesley, 1999
we want to filter by Customer types
the product owner wants to have a filter by subscription as well
What is the motivation at (1) to introduce the
ISpecification<T>
concept?There isn't any.
Introducing it at (1) requires prescience that (2) will be required in the future. However time and time again developers have been shown to be terrible at predicting future changes, often increasing complexity to gain generality that never pays off or worse gets in the way of change that is needed later.
The more realistic scenario is that the simple version is implemented at (1) and that
ISpecification<T>
is introduced at (2) - which means that nothing was ever "closed to modification".Kevlin Henney's rant about the Open-Closed Principle.
The idea behind this article is that we should write clean code from the beginning and the scenario i described was just to introduce the problem and show that the first code written was not good enough to follow the design principle.
If we talk about the efficiency of the SOLID principles, we have pros and cons but for me the pros are more than cons.
Thanks for your comment.
The final code only makes sense when both requirements (1, 2) are known.
Introducing
ISpecification<T>
when only (1) is known is a guess. It may be an educated guess but the investment doesn't really pay off until (2) materializes - before that the complexity it introduces is non-essential.Compare that to
It acknowledges that earlier we may have had insufficient information to create the optimal design (to grant the freedom to be changed in the way needed) but now that we know what needs to change, we adapt to reflect our new state of knowledge.
Our version 1 "state of knowledge" simply leads to
And version 2 might simply be
At this point we may simply decide that duplication is preferable to the wrong abstraction and just leave it.
If we do decide to go ahead with
ISpecification<T>
because some followup conversations with the stakeholders strongly suggested that there may be other filter types needed later but some code we don't have access to already usesFilterByType
we may even end up withSo the real reason to be cautious with the Open-Closed Principle is when it requires you to predict the future - because when you predict the future you'll likely be wrong (YAGNI).
"Be careful when choosing the areas of code that need to be extended; applying the Open-Closed Principle EVERYWHERE is wasteful, unnecessary, and can lead to complex, hard to understand code."
Head First Design Patterns; Eric Freeman & Elisabeth Freeman, 2004; p.87
Robert C. Martin wrote about OCP again in 2014 - essentially advocating a Plugin Architecture. When you design a plugin architecture you have already decided up front along which lines your system needs to be extensible (supported hopefully by good evidence of that necessity/benefit). So OCP is presented as a principle for systems design - not class design.
Also note that
ISpecification<T>
has exactly one method. As Scott Wlaschin observes function types take SRP and ISP to the extreme.So given what C# is capable of nowadays
seems like a much less cumbersome way to move forward.
I completely agree with you on YAGNI.
Also i know that applying the Open-Closed Principle EVERYWHERE is wasteful, unnecessary, and can lead to complex, hard to understand code and fully agree with it.
But seriously I’m not a fun of duplicated code and the is a lot of solutions to avoid that !
The idea behind my article was to explain how we can implement the OCP principle inside a software in different ways. This is an article to simplify things to software developers in my point of view.
And in the conclusion a noticed that :
I can’t see what was wrong ?
To conclude :
I think, we both agree that SOLID are principles and not rules!!
Thank you for these comments which have raised this thread and i hope that it will be conclusive debate.
DRY isn't about removing repetition or duplication: "every piece of knowledge must have a single, unambiguous, authoritative representation within a system". Sometimes code can look similar but is unrelated.
(POV: Development by Slogan with DRY: Part 2, The Tower of Coupling)
The setup.
If the article would have started by stating that it was known up front that both
ByType
andBySubscription
filtering would be needed and that other, yet to be determined kinds of filtering may still need to be added in the future, that would have laid out the background of a clear and necessary dimension of extensibility that OCP could apply to.As it is, it was presented as if OCP somehow should make it obvious which dimension of extensibility is needed when only
ByType
filtering is required. Initially there are no actual dimensions of variability indicated. That only happened once theBySubscription
requirement surfaced later.This sets up the unrealistic expectation that
IFilter<T>
should be introduced at the very beginning (which is revisionist) when in fact the value of that abstraction only becomes clear much later.In a realistic scenario you would start with
FilterByType
and end up atApply
- which means that the code isn't "closed to modification" as you are "opening it for extension".In my example i apply OCP just in time and this what should we do and i Refactor to Open-Closed.
YAGNI
The Open Closed Principle - The Clean Code Blog by Robert C. Martin (Uncle Bob)
Protected Variation:The Importance of Being Closed
[2]Hall, 1988. [2] Refactoring, Martin Fowler, Addison Wesley, 1999
The Open Closed Principle - The Clean Code Blog by Robert C. Martin (Uncle Bob)