Next lesson playing in 5 seconds

  • Overview
  • Transcript

3.3 Decorator

Inheritance is a common pattern, but it can sometimes clutter your code base. In those cases, a decorator may serve you better.

1 lesson, 01:40


2.Creational Patterns
2 lessons, 11:52



3.Structural Patterns
4 lessons, 33:47





4.Behavior Patterns
4 lessons, 36:32

Chain of Responsibility




1 lesson, 01:06


3.3 Decorator

The decorator pattern allows us to add functionality to an object without modifying that object. And it also allows us to write more simple code. Well, let's just look at an example. So we're going to have a class called Circle. And then we're going to have another class called Triangle, and that's great. And of course, we would have classes for the other shapes. But then let's say that's okay, we want a circle that has a border around it. So, what we could do is create a new class called BorderedCircle. And this, of course, is going to extend Circle. Because we want to reuse that functionality, and that's great. But you know, we also want a border for our Triangle, and Rectangle, and all of our other shapes. So we have to write classes for all of those. But then, let's say that's okay, we want to have a red circle. Well, that's great. So let's have a class that's RedCircle, that extends, well what do we extend? Do we extend just the normal Circle, or do we extend the BorderedCircle? Well, you can see the problem that we can get into. We can have a ton of classes, just for adding new functionality to what was originally a fairly simple object. Now, traits in PHP can get around this. And in fact, some people say that traits violates the whole idea of decorating, or the decorator pattern. But really traits are decorators in some way. So we could use traits here, but we're going to use just strict object-oriented ideas. So, what we could do with a decorator pattern is do this. We can start off with an interface called IShape. Because that's essentially what we have, we have different shapes. So, we want at least some relationship between all of these different shapes. So we're going to have a method called draw. So our circle is going to implement the IShape interface. And we're going to have a public function called draw, that is simply going to return Drawing circle. Of course, in a real application, this would actually draw a circle. But we're not going to go that far here. And we will essentially do the same thing for the Triangle. So let's just add that, but instead of drawing a circle, we will draw a triangle. So we have our classes. But now, we want to add some functionality to them. And the way that we do that is with a decorator. We don't extend these base classes. Instead, we write our own base class for a decorator. So let's start with an abstract class. And we're going to call this ShapeDecorator, because we can have a variety of decorators. We could add color, or add a border, or rotate. Or really, anything that we want to add to one of these shapes, we could do that with a decorator. So we're going to start with an abstract class, that we can then use as a base class for all of our other decorators. But this is going to implement the IShape interface as well. Because these are still shapes, they are just going to wrap around existing shapes. So that's essentially what a decorator is, it is a wrapper for a similar object. So we need to store the wrapped object, so we're going to have a protected shape. And inside of the constructor, we are going to pass in that IShape object. So we will have our shape, and then we will set this shape equal to the provided shape. And then we will have our draw method, but this is going to be abstract. Because each decorator is going to have its own draw method. There's no sense in implementing that here. So, let's start with a bordered shape. So, we will have a class called BorderedShape. We want to extend our ShapeDecorator class, and then we will implement the draw method. So, what we could do is get the result of our inner shape. So we will say, this-shape-draw(), and then we're going to use that with a new result. So, we're going to say result is bordered. Or, since this is a BorderedShape, we can say that this is bordered. Well, no we can't, because we're saying drawing Circle, so let's just do this. We'll say result is bordered, or has border. There, that would be good. So now, we would do it like this. We would create our circle, we would new up our circle object. And let's go ahead, and let's echo out Circle, and then draw. And then we will see what that does in the console. We of course, well, we would hope that we would see drawing circle, and we do, great. So, now what we would do is, decorate this circle. So that we would say circle equals, we would new up our BorderedShape. And then we would pass in our Circle. So, while we are changing the object that is stored within our variable. We aren't modifying the circle that we are passing to BorderedShape. If you'll remember, all we are doing is getting the result of calling the draw method, and then adding to that. We're not changing anything about the existing shape. So now, we should see, drawing circle, has border. And of course we don't. So it's because I didn't save the file. We saved, we run. And then the drawing circle has a border, great. So now let's write another decorator. So this is going to be RedShape. And we will extend the ShapeDecorator class, and then we will implement our draw method. So that we will get the result of calling this-shape-draw(). And then we will return, the result is red. So now we can take our existing circle, which is a BorderedCircle. And then we can decorate that with the RedShape decorator. So now we should see, drawing circle, has border, is red. So you could see where we're going with this. But the beauty of this, is that we have these decorator classes, and we're using them with a Circle. But we could also use them with a Triangle, and we could use them with any shapes. So if we wanted to write a Rectangle class, or some other class, we could, and then we could use these decorators. So just to prove that, let's take our existing code for Circle, and then let's just change this for a Triangle. We, of course, need to change all of these references, but that won't take very long. And then we will have a red bordered circle, and a red bordered triangle. So let's save, let's go to the command line, and there we go. We have the drawing circle, has border, is red, and then drawing triangle, has border, is red. So, the decorator pattern allows us to add functionality to an existing object without modifying that object. By wrapping around that object. And by doing so, we write more simple code, and of course, less code as well.

Back to the top