The second principle from the solid design principles that we're going to take a look at is the open
closed principle.
So let me give you an example let's suppose you are making a web site which sells certain products and
your manager wants you to be able to filter those products.
So let's suppose that each of the products has certain traits like for example color and size so we're
going to define those as enough.
So we're going to have a caller.
Let's say red green blue and we're also going to have size which is going to be small medium and large
for example so we can define a product and the product is going to be defined in terms of its name as
well as the color and the size of the product.
So what you want to do is you want to be able to filter things.
Now if you just go ahead and define a simple product filter what you can do is you can of course perform
filtering on any particular criteria.
So let's suppose your manager asks you to filter by color and you do something like the following.
If we use vectors of pointers here you'll have a vector of product pointer is called by color so this
is going to take the set off items that you want to filter as well as the color you want to filter by
and the implementation is actually easy so you make the result.
And then for each of the items so for auto write in items you check whether the item fits the particular
color.
So if I color is equal to color then you add it to the result.
So resolve that push back and you return the result.
So that's a very simple furter and you can start using this filter straight away so we can make a bunch
of products here.
Let's actually make just maybe two or three products so you can have something like the following.
Here I have a product called Apple which is called apple green and small tree which is green and large
and also a house which is blue and large.
So you can make a vector of pointers to these products.
Vector product pointer is called items.
Just taking the addresses off apple tree house and we can start filtering them using our product filters
so you make a product filter P.F. and then you say auto let's say green things green things.
So you say after all bicolor.
So you filter by color and you specify the items and the color green.
And this way you can actually print things out.
So I can save for photo item item in green things and I can print that this particular item is green
so I can see out for example that the item name here is green like so we can we can run this.
This actually works there's no particular problem with this implementation up until the point where
you start requiring the criterium because in the moment we can only filter by color.
That's the only thing that we need to do right now.
And sure we get apples green and trees green so everything is more or less OK.
But let's suppose that your manager comes back to you and says Can you also please filter by size and
what you do is you end up basically copying the code that you've written and you end up making yet another
copy where you specify all we're going to filter by size now.
So filter by size obviously we're going to have the size here and now we perform the check here.
So this is once again a workable solution but the problem is you've had to go back into a chunk of code
that you've already tested and not only have your you tested it may be you've already shipped it to
the client.
So now you have to introduce this binary compatibility by adding another interface member to proc filter
which isn't so great.
And somewhere down the line of course your boss is going to come back to you once again and say Can
you please make sure that our clients can filter him by both size and color.
So you go back into an existing code you replicate this.
You say size size color or color you rename the function so it's going to be obviously now it's going
to be by size and color and you specify additional criteria down below.
Let me just break this so you specify that you want to filter by size and you also want to check the
item color to be equal to color.
So this is once again it's a problematic approach to this problem because we have two criteria and we
are they have three functions and we have three criteria and we would have eight functions and you can
see where this is going.
Generally it doesn't scale and you don't really want to go into an existing code.
And this brings us to the open closed principle.
So the open closed principle basically states that your systems should be open to extensions so you
should be able to extend systems by inheritance for example but closed for modification.
And unfortunately what we're doing here is we're modifying existing code instead of inheriting and thereby
extending the system so let's see if we can build a better filter.
And we're going to do it using the specification pattern which is an enterprise pattern a pattern related
to data and working with data.
So the way we're going to do this is by defining two interfaces we're going to define interfaces called
specification and filter.
So first of all define the specification interface so template type name T and those is we using t so
it doesn't just have to work on products it can work on any kind of type and it's going to be called
specification and all we gonna do here is add a single pure virtual method called is satisfied it's
going to return and boolean is going to be called is satisfied and all it tries to do is it tries to
check whether some item satisfies a particular specification and the specification is once again going
to be defined through inheritance.
Now the second part of the puzzle is yet another template class and this one is going to be called Filter.
Now filter is going to define the interface that needs to be implemented by any kind of filter like
a filter of items for example.
All we really do here is once again we define a pure virtual member which in our case is going to return
a vector of two pointers that's the result of our search and is going to be called filters so here we're
going to take two arguments.
The first is a vector of pointers specifying the items that we want to filter.
And the second one is that the specification that we want to filter upon so specification of t once
again.
So that's the template argument I'll take it by reference.
Call it spec and is once again going to be pure virtue.
So this is the interface that needs to be implemented by whoever wants to actually build a filter.
Ok so we have two interfaces we can actually start using them to build filters off products so first
of all make a a better filter of product so let's call it the better filter.
So a better filter is going to be a filter of products.
Notice I'm replacing the template argument buy a product here.
And all I have to do now is go into co-generation and implement this particular member.
So by and large the implementation signature here is correct but notice it generated 80 here that should
be a product.
But apart from that we've got a signature that we can work with and all we have to do is the same thing
that we did previously when we worked with our specific search mechanism.
So here we have filtering by size by size and color.
Now we're going to be very generic because the actual specification of what to filter by is taken as
a reference so once again all we have to do is we specify the vector off product pointers as the result
which we return at the end of the function.
And here we go through every single item.
So for hoto item in items and we check whether the item conforms to the specification.
So here we say if the specification is satisfied by this particular item that what we do is we add that
to the result.
So a result of push back item and that's pretty much it.
So that's our filter.
Now what we have to do is we have to specify the different specifications that we want to be able to
use to actually search so for example we want to search by color one to search by size.
We want to search by size and color.
So if I wanted to call a specification I would define it like follows so I have color specification
and that's going to be a specification on a product.
So notice I'm specializing the template here and now I specify the color that I want to filter by.
I might take it in the constructor for example so generate the constructor which actually takes the
caller.
And then of course I have to implement the interface.
So if we go into cogeneration Once again I go into implement functions implement that is satisfied and
here all I have to do is check the item color the item color corresponds to the color I'm filtering
by.
So this is how you would make a color specification now instead of writing all of this we can rewrite
this using our better filters so it's a better filter B.
And we make a color specification called Green which is going to just take the color green here and
then what we can do is we can go through each of the green items so for auto item in BFO filter with
items and green so notice we are specifying the first argument as the list of pointers and the second
as the specification and once again we can see out each of the item names followed by is green as before
you use green like so and we can actually run this and see what we get.
So in a similar fashion to how I've implemented the color specification you can of course make the size
specification is what.
It's very easy you just have a different criterion.
So here we have apples green trees green So as you can see everything is working nicely here.
And similarly for the size specification I'm just going to drop it here a very similar idea.
So you have a size as a member.
You take it in the constructor and then you have the override which actually checks this particular
specification is satisfied by a particular item by checking the size.
Now the only thing that's remaining is to see what's happening in terms of combinations.
Because here we have by size and color.
So how would you implement something like this using the specification pattern that we've just implemented.
Well it's actually rather easy.
There's no particular problem because all you do is you make a combinator.
So once again we're going to have template type names t it's going to be extra cold and specifications.
No just our specifications up above like specifications does not generic.
They are.
They are very kind of predefined to work on products now and specification is going to be very generic
and is going to inherit from specification of T.
So this is how we're going to have it.
And here we just need to take the arguments of the left and right specifications and combine them together.
So here what I would do is I would simply have a specification of C reference code first and another
called second and then maybe take them in the constructor so we just go ahead and generate constructor
which takes both of these and simply assigns them to the appropriate members.
Then what we can do is we can perform the appropriate override so if we go in here and we actually make
the appropriate override for it is satisfied the implementation is satisfied is to simply check both
of the specifications and combine them together.
So we told the first is satisfied item and second is satisfied item.
And this allows you to basically if you want to find for example all the green and large things what
you can do is you can make the following so you make a size specification called large which says size
large.
And then you make the specification and specification notice you need the product name here because
it's a template.
It's a template class essentially.
And this would be something like green and large.
And I need to learn how to spell So this is going to be green and large and it would be combining the
green specification notice I get a pop up here and the large specifications.
So we now have the specification Combinator and once again what we can do is we can sort of copy of
the code that we've written before and just use a different filter here so instead of green I can use
green and large.
And here I can type is green and large and we can not try running this and just seeing that our specification
Combinator is also very functional and does a kind of what we wanted to do essentially.
All right so as you can see only the tree is green and large and everything works correctly everything
works as before.
Now if you want to kind of shorten this because I know that some of you may be particularly upset about
having to initialize one to three four different variables you can make a lot more compact so one of
the things I would do typically is I would start defining maybe just a little bit of a custom operator.
So if we go into the specification base class if I go into specifications here what I would typically
do is I would define maybe the and operator.
So you can have an specification operator and which takes a specification of t of some kind code other
and you will simply return an and specification constructed of yourself as well as whatever other specification
is provided.
So what this lets us do is it lets us perform the green the green in large filtering in a much more
compact way because what you can do now is you can write something like the following You can say auto
spec equals color specification color green color green like so and then use operator and and specify
the size specification here is size specification size large for example and this will be the combined
specification and then you would use that in here.
So it's a lot shorter because you don't create any of these extraneous variables.
And as you can probably guess the result of the execution of this code is pretty much the same.
So just to reiterate what I've already stated the goal of the open closed principle is to avoid having
to jump into code that you've already written.
That's that's all it says.
Open for extension meaning that you can always make and use specification by extending by inheriting
effectively but closed for modification which means that in the context of the specification pchter
you would never require yourself to go back into the filter interface or the let's say the specification
interface it wouldn't have to change those you would extend them by inheritance and then you would create
maybe these combinators like we have and specification you can make or specification and so on and so
forth.
So it's very flexible notice that we also have a template argument so we're not constrained ourselves
to products.
We can also have other kinds of items in here and it's all very nice and flexible and very usable as
well.
So open for extension close for modification that's the goal of the open closed principle and I've managed
to show you an enterprise pattern which is called the specification pattern as well.