Lecture thumbnail 0:00 / 0:00 All right.
So there is one problem with the dynamic decorator, and that problem is the problem of cycles or repetitions.
And here is what I mean.
Let’s imagine that we have a circle and then we make a red circle, and then we decide to do something
stupid.
We decide to make another colored shape decorator and we decide to make this circle blue as well as
red.
So we say var colored too.
And if we call this one colored one, so color two would be a new colored shape where we take colored
one and we give it a color blue.
So we’re trying to do something which doesn’t really make sense.
We are making a decorator which colors the circle red, and then another one which colors the circle
blue.
Now, at the moment, we don’t have any protections against this.
And this means that if we do decide to writeline the information about the color to object as string
like so we’re going to see something that doesn’t really make that much sense because we’re going to
see the output.
A circle of radius two has the color, red has the color blue.
Probably not what we want.
So the question is, how can we actually manage this situation?
Is there any way for us to prevent the user from actually applying these kinds of decorators?
Now, as you can guess, there is no way to statically check that somebody is applying the same decorator
twice.
These are dynamic decorators.
They are being applied at runtime and so we don’t really know.
But it is possible to check these things at runtime.
And I’m going to show you a rather overengineered, shall we say, way of handling this situation.
But for the sake of demonstration, I hope it shows how to build complicated systems where such functionality
is in fact required.
So what we’re going to be talking about is different policies of repetition.
So you are able to have a colored shape inside a colored shape.
The question is how do we want the program to handle this kind of situation?
So what we’re going to define is a policy class.
We’re going to define some sort of base class for how these things are going to be handled.
So I’m going to have it public abstract class called shape decorator, decorator cycle policy.
Now, the thing about decorators is there are two locations where you can check that one decorator is
cycling or repeating itself.
One is during the construction.
Like when you call the constructor here, you can check that the constructor already refers to an object
of a similar type.
And of course, if we had to do this just at one level, then you could certainly do something like
this.
If shape is transparent shape, then throw some exception, throw new invalid operation exception,
whatever you invalid operation except some something like this, you could do this.
But the problem is that it doesn’t really prevent us from cycling things.
So if I have a colored shape of a transparent shape of a colored shape, I’m still applying the colored
shape twice.
It’s just not immediate.
And as a result, we have to be more sophisticated about the way that we actually handle these things.
So inside the shape decorator policy, like I mentioned, there are two places where this repetition
can happen.
It can happen at the construction level where you are constructing something and you might be repeating
or cycling a particular decorator or at application level.
That’s when you call some method on the decorator.
And this method actually goes through all the motions.
And this is where you might want to, for example, not so much throw an exception, but for example,
just swallow the extras.
So if you apply the colored shape twice like we do here and here, maybe the first colored shape decorator
does get applied, but the second one does not.
So you want this kind of policy as well.
So we’re going to have two methods inside the shape decorator policy.
We’re going to have an abstract boolean method called type addition allowed type, type I list type,
all types.
All right.
So I’m going to explain what this is in just a moment and we’ll have a second method.
And the second method is going to be called something like application allowed.
All right, So what does these two methods actually do?
Well, type addition allowed is when you call the constructor with something that’s already been met
somewhere down the line, and this is where you can return true or false, depending on whether this
is actually an allowed operation.
So if it’s true, then everything is fine.
You can go ahead.
If it’s false, then, well, you have to handle it somehow.
You have to do something about it.
Maybe throw an exception or maybe, I don’t know, crash an application, for example, or maybe just
not bother adding this particular element to the set of elements that we are tracking.
Because unfortunately, as we apply one decorator over another decorator and so on and so forth, we
have to have a list of those types.
And it’s precisely those types that will appear as the second argument of these two methods.
So the first argument will be the type we are currently in, the type that we are trying to add to this
chain of decorators.
And the second is all the decorators that have been applied to that particular type.
And this is where we can detect repetition and maybe do something about it.
So.
Is the question, as always, is how do we actually handle all of this?
How do we traverse this entire structure?
Because it is going to be rather complicated business, I’m afraid so.
First of all, let’s define some policies.
So this is an abstract base class.
We can define a bunch of policies for how we want to handle things.
And the first one is I’ll just have a policy which throws an exception when you try to cycle a type.
So we’ll have a class called Throw on Cycle Policy, which inherits from shape decorator cycle policy
like so.
So let’s go ahead and implement this.
Now both of these operations, both the type addition allowed and application allowed are going to basically
invoke the same thing.
And that same thing is going to be some sort of private boolean handler where you take the type, type,
type, type and a list of all the types inside this chain, all types.
And we basically say the following.
We basically say that if all types already contain the type that we’re trying to add, then we throw
a new invalid operation exception Inside the exception, we might want to say something like cycle detected
exclamation mark type is already a and then we say already a type dot full name like so exclamation
mark and then we return false.
Well, actually we’re throwing an exception, so it doesn’t really matter.
So we return.
True if everything is okay.
So I’ll just close the round bracket here.
We return true if everything is fine.
Otherwise we throw an exception.
There is no situation where we actually return false from this.
So then this handler actually gets called from both here as well as here.
So we call handler with type and all types and I’ll just copy this and paste it here.
So whether it’s application or addition, we just throw an exception.
And by the way, yeah, that has to be a return because we still return some boolean value from both
of these.
So that’s one way of handling things via a policy.
We’ll add a couple of other policies later on, but for now we want to start building decorators again.
And remember, we already have decorators, we have transparent shape and we have color shape, but
they inherit from shape.
And what we want to add is we want to add this idea of cycle detection in a base class.
And in actual fact, we’ll have two base classes, not one.
This is something that you experience quite often when doing net development, especially development
of libraries, where you make a type in both its generic and non generic versions.
So what I mean by this is you make a class called foo and then you make a class called foo of T, which
actually inherits from foo.
You see this quite often in dotnet development and the reason is the is keyword.
So it’s very difficult for us to write something like if X is foo like this.
So we always have to specify a type here we have to say foo event.
We cannot say if x is foo of just an open type now.
Yeah, you can check this using reflection, but then what you really want to do is you want to also
give it a name like F and you absolutely cannot do this.
If it’s an open type, it’s just really difficult to work with.
So this is why we have this duality.
So we have a base class foo and then we also have another base class foo of T which inherits from foo.
And that’s exactly what we’re going to do in our implementation of the shape decorator.
So the idea is that we’ll have a public abstract class called Shape Decorator, which is going to inherit
from shape.
And here what we’re going to do is we’re going to store the list of types that have been decorated.
So remember, it’s a decorator of a decorator of a decorator, and unfortunately, we cannot make them
private.
So I’m going to make them protected.
Internal read only list of type types like so.
Okay.
So that’s our list of types.
And of course, since this is a base class, this is where we can also add that shape that we’re actually
decorating.
And once again, remember that shape can be a shape decorator because the decorator itself inherits
from shape.
So here we say protected internal.
Once again, I cannot make this private and you’re welcome to inspect the application later on to make
sure that I cannot make this private.
So shape, shape like this.
And then we make a constructor.
Inside the constructor, we simply take a shape that we’re decorating as before we assign the shape.
This shape equals shape.
And then we do something interesting.
Now, remember, shape can quite possibly be a decorator because shape decorator inherits from shape.
So what we can do is we can check this and if for some reason shape is a shape decorator, let’s call
it as D, then what we can do is we can take the types and we can add all the types from whatever object
we’re decorating.
So it’s a kind of recursive operation.
So we take s d dot types.
And notice here, I’m saying dot types, which is protected internal right here.
So we’re taking all of those types and we’re adding them to our current list.
And having this list is very useful because then we can inspect what’s in this list and whether there
are any cycles in this list.
Okay.
And now we make the generic version.
So this is the non generic shape decorator.
And now we have another abstract class called Shape Decorator.
And this is a generic version decorator.
So there’s going to be two generic arguments here.
The first is T self.
So we’re doing recursive generics as we’ve done in many parts of this course.
And the second one is the cycle policy that we want to apply the cycle policy.
So this is going to inherit from Shape Decorator.
And we also have a restriction.
So T cycle policy has to inherit from shape decorator cycle policy and it also has to have a default
constructor.
Now there are different ways of handling it, but I’m going to require that it has a default constructor.
So let’s implement the actual members.
There’s only one member that needs to be implemented.
That is the constructor call.
Remember, the base constructor here takes a shape, so we have to call it here.
No way around this.
Now what we want to be able to do in the constructor is of course to check the policy.
Is this kind of type edition actually allowed?
So we say if according to the current policy and by the way, here is a question, where do we set the
policy?
We initialize it right here.
So here we can have a protected read only t cycle policy policy equals new.
This is why we have this new requirement up here so that we can call it down here.
Now, coming back to the constructor, we basically say if policy type addition allowed.
So if we are allowed to add type of t self to the list of types, then what we do is we say types dot
add type of types dot add, not add range type of t self.
There we go.
So we add ourselves to that list of types.
And by the way, T self is the most inherited type, so to speak, because we’re doing recursive generics.
So this is going to be inherited later on and you’ll have, for example, a colored shape and that is
that is what would be passed inside t self.
Okay.
So we have this base class.
And by the way, one thing you might say to me is, well, why do we have to define a policy inside
every inheritor?
Because now what happens is you have to define a policy inside every like if you want a colored shape,
you’d say public class, color shape, and then you would inherit from shape decorator and you would
say, well, we’re going to have color shape as the actual type.
Remember, this is what corresponds to the t self argument right here.
And the second would be throw on cycle policy.
So we want to throw exceptions whenever somebody is repeating something.
So generate the constructor here and right.
We already have a colored shape here.
Let’s get rid of this as much as we can.
Although I do like the I do like the override here.
So let me actually we want to keep the constructor as well.
We want to keep most of these things.
We want the color.
We want just about everything.
So let’s put this inside our new color shape and get rid of the old colored shape.
And now we can fix a few things because obviously there needs to be a base class call here with shape
like so and well, so one question you might have is why do we define a policy here?
Well, if you want to define a policy globally, there are different ways of doing it.
So if you want to define a policy globally in a single location, just up here, for example, you can
have yet another base class.
So we’ve already made two base classes.
It might feel like, well, it’s a bit crazy to add the third base class, but you could do something
like the following public class shape decorator with policy where you would only take the type argument
t and you would inherit from shape decorator T comma throw on cycle policy.
If that’s the default policy that you want to use this is how you would do this.
And then instead of inheriting from shape decorator like here, what you would do is you would inherit
from shape decorator with policy and just provide color shape as the reference to t self.
So that is one way of handling it.
And if you want to have it inside your dependency injection container, being able to set this policy
globally, then you would have to define things differently.
Then you wouldn’t be able to necessarily do it this way.
But you might also want to define the policy as a dynamic parameter that you would take right here in
the constructor, which is not very nice to be honest.
So we’ll stick with this idea for now.
So we have just to recap, we have a colored shape, which is a decorator.
It’s a shape decorator which refers to color, shape and throws whenever there are any cycles.
So now maybe we want to actually test this, but before we test this.
Well, actually, no, because.
Because we throw exceptions, we can already test this this entire application because it’s supposed
to fail because now we have a colored shape of a colored shape.
And, well, we have some code.
If you look up here inside the the actual policy, let me just find it in a moment.
So we have a throw on cycle policy which basically checks that if the type that we’re trying to decorate
is already contained in all the types, then we throw an exception.
And that’s precisely what should be triggered as we try to perform a colored shape on top of a colored
shape.
So let’s run this.
Let’s take a look at what it is that we get as we execute all of this.
And as you can see, that’s exactly what we are getting.
We’re getting an exception, an invalid operation exception.
And it says cycle detected type is already blah, blah, blah.
It’s already a colored shape.
So.
So that’s that’s basically.
All right.
So that is one policy.
But of course, now we have a customizable kind of structure.
And by the way, this whole policy thing is basically the strategy pattern.
So we’ll talk about the strategy pattern in a different section of the course.
But this is it.
This is a strategy.
So now we’ll build a different strategy.
Let’s suppose we build a strategy which allows everything.
So it allows you to repeat the decorators as much as you want with as many cycles as you want, and
nobody really cares.
So in order to do this, well, we first of all will find the decorator policy which throws and we’ll
build another policy.
So public class, I’m going to give it some meaningful name.
How about how about cycles allowed Policy cycles allowed policy shape decorator cycle policy.
So here we return true in both cases, which means we completely allow everything return true and return
true.
So this is going to work just fine.
And if we run this, you’ll see that the program executes without any problems.
Well, actually, hold on.
It’s it’s still throwing an exception because.
Well, we didn’t change the settings.
So let’s go in.
Let’s find the class which actually defines the setting, which is here.
And let’s replace throw on cycle policy with um, what was it called again?
Uh, da da da da.
It was called I wish I call it something like do what you want policy let me trying to cycles allowed
policy.
There we go.
Okay so if I run this now everything should be as before.
Basically kind of silly repetition.
Yeah.
Circle radius two has the color, red has the color blue, but.
The third policy is the one that’s really interesting.
What I want to show you.
The third policy would basically allow us to apply the decorator or rather initialize the decorators,
but apply them only once.
So we only apply the decorator the first time it’s met.
And for this we need to modify a couple of things.
We need to modify the actual decorator.
So coming back to, let’s say, color shape right here.
So what we want to do is we want to replace as a string and do it differently, change it to a statement
body for now.
So we’ll make a string builder var SB equals new string builder and string builder is going to contain
the first part of this.
So it’s going to be shape as string.
So we call the underlying, whatever the case may be.
So we say shape as string and that’s pretty much it.
And then we only add the color if the policy allows us to apply the color.
So we say something like the following if policy dot application allowed and here is the interesting
part.
So at this point in time types has already been initialized.
So at this point in time, types at position zero is the current class and types at position one and
all the way to the end is what it wraps.
So we essentially have to check the types at zero is allowed within the list, which is types dot skip
1.2 list.
So this is somewhat convoluted.
So if this is in fact allowed, then we do append and we say has the color color, otherwise we don’t
return SB dot two string.
So this is the second method of the interface.
Remember the abstract class that we used had application allowed as the second element, this one.
So in cycles allowed policy you’re returning true, meaning everything is okay, but we’re going to
make a new policy.
We’re going to make a policy which we’re going to call absorb.
So in this policy, let’s make absorb cycle policy, shape decorator cycle policy.
So in this policy, what we’re going to do is we’re going to allow the construction.
So when it comes to type additions, we’ll return.
True, we’ll allow people constructing this thing.
But when it comes to applying this thing, we’re only going to allow it if all the types do not contain
the current type.
So we say return.
Not all types dot contain type.
So this check is going to fail.
Whenever we see a cycle, whenever we see a decorator being applied twice.
So with all of this written down, what we can now do is we can change the policy inside our color shape.
So if we go up to here and once again replace this policy with a absorb cycle policy, what should now
happen is, as we call as string here, the first decorator, the red decorator is the one that gets
applied and the blue decorator does not.
So let’s actually run this.
Let’s take a look at what happens right now.
Ta da!
A circle of radius two has the color red.
And notice nobody gets to apply the color blue because we have this additional check.
All right, So in this demo, I’ve done a bit of overengineering, I would say, because I’m trying
to solve a very niche problem of applying multiple decorators, but sometimes it’s relevant.
Sometimes you have to do things like this, and if that’s what you need to do, then you need to build
this kind of complicated structure.
And in fact, I can think of ways of making it even more complicated.
But this idea of cycles, if you want to handle them, this is how you would actually handle them,
at least for the dynamic decorator, because unfortunately there is no way to statically detect any
kind of repetitions like this unless of course you write your own custom analyzers.
And then of course this whole thing is a dynamic construction.
So you could be constructing these decorators at runtime and not compile time, in which case you wouldn’t
detect them at compile time anyway.
So hopefully this has been an enlightening demo.
Play Play Play Play Play Play Stop Play Play Start Play information alert