Design Pattern Principles for ActionScript 3.0: The Open/Closed Principle

bucketrule
In the little AIR menu with the 10 principles one of the clearest is the Open/Closed Principle. At one time this principle suggested that all updates be created using an implementation or extension of virtually any class. That could get tricky, especially if someone understood that to mean implementing an update or extending a subclass. However, later, the extension came to mean the extension of an abstract base class. In other words the interface is extended but never modified. Given these caveats, we can understand the basic principle as it is now understood:

Classes should be open for extension but closed for modification.

Easy to Take to Work

(Note: In talking about a program and changes, we’re not including the Client class. It just makes requests, and you can add requests and change them all you want in the Client.)

The idea that when you want to change a program, the only way you are able to make changes is by extension may seem a little restrictive. However, what the principle is really doing is providing a way to make changes without having to rewrite the whole program. The dictum, New behaviors are only available through extension should not be phrased in a way to make it sound like it’s tying your hands. Rather, it should say something like,

Hey! The Open/Closed principle makes it easy to add new behaviors without having to mess up your whole program.


Looking at the principle in this new light, we can see how it works with a simple example. Suppose you’re doing a lot of work with Shape class objects. Right away you need circles and rectangles, but you might need other shapes later. So to get started, you set up your classes that you know you’ll need, but you want to leave the door open for easy extension. To get started you create an abstract class that leaves an abstract method to add different shape types. The following script is your initial starting point:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package
{
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.errors.IllegalOperationError;
 
	//Asbstract class do not instantiate
	public class IOpenClosed extends Sprite
	{
		protected var shape:Shape=new Shape();
 
		protected function makeShape(rw:Number,h:Number,c:Number):void
		{
			throw new IllegalOperationError("Abstract method: must be overridden in a subclass");
		}
 
		public function setShape(fillCol:uint,borderCol:uint,lw:Number,rw:Number,h:Number,c:Number):void
		{
			shape.graphics.beginFill(fillCol);
			shape.graphics.lineStyle(lw,borderCol);
			makeShape(rw,h,c);
			shape.graphics.endFill();
			addChild(shape);
		}
	}
}

You will notice that the makeShape() function in the setShape() method only has three parameters. That’s because the first two parameters are the x and y positions. These will be set to 0 using a literal. The stage position is placed using an instance of the shape.

Next, two little classes extend the abstract class to make the circle and rectangle. First the circle-making class extends the abstract class and then overrides the makeShape() function to add the graphics method for creating a circle .

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
package
{
	public class OCround extends IOpenClosed
	{
		override protected function makeShape(rw:Number,h:Number,c:Number):void
		{
			shape.graphics.drawCircle(0, 0, rw);
		}
	}
}

Next, we do the same thing for a rectangle.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
package
{
	public class OCrectangle extends IOpenClosed
	{
		override protected function makeShape(rw:Number,h:Number,c:Number):void
		{
			shape.graphics.drawRect(0, 0, rw, h);
		}
	}
}

Okay, we’re all done. We’ll create a Client class to test it:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package
{
	import flash.display.Sprite;
	//Parameters for setShape are:
	//setShape(fillCol:uint,borderCol:uint,lw:Number,rw:Number,h:Number,c:Number):void
 
	public class Client extends Sprite
	{
		private var rec:IOpenClosed=new OCrectangle();
		private var cir:IOpenClosed=new OCround();
		private var rndrec:IOpenClosed=new OCroundrec();
 
		public function Client()
		{
			rec.setShape(0xC03000,0x32331D,4,100,60,0)
			rec.x=50, rec.y=50;
			addChild(rec);
 
			cir.setShape(0x787746,0x40411E,4,50,0,0)
			cir.x=100, cir.y=200;
			addChild(cir);
		}
	}
}

When tested, we can see that we get our shapes as shown in Figure 1.
oc1

Figure 1: Two shapes created with common interface

Obviously not rocket science, but it’s a handy tool for setting up different kinds of shapes.

What about Rounded Rectangles?

This part is the important part as far as both the Open/Closed principle and design patterns are concerned. Suppose you decide you need a rounded rectangle. With the Open/Closed principle in mind, you cannot modify the code but you can add to it through extension. So, let’s add a rounded rectangle:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
package
{
	public class OCroundrec extends IOpenClosed
	{
		override protected function makeShape(rw:Number,h:Number,c:Number):void
		{
			shape.graphics.drawRoundRect(0, 0, rw,h,c);
		}
	}
}

That wasn’t hard. Add the following lines to the Client, and let’s test it:

?View Code ACTIONSCRIPT
1
2
3
4
5
private var rndrec:IOpenClosed=new OCroundrec();
….
rndrec.setShape(0x32331D,0xC03000,4,100,60,24)
rndrec.x=200, rndrec.y=50;
addChild(rndrec);

Figure 2 shows that a rounded rectangle has joined the circle and the rectangle on the stage:
oc2
Figure 2: A third shape is created through extension.

As you can see, while it was more work to set it up initially, it’s a lot less work to create new shapes. Also, requesting the shapes in the clients is very simple as well.

Into the Lunch Bucket

Here’s a handy and simple principle that you can see reflected in many different design patterns. For example, the Decorator, Composite, and Builder offer change through extension. So, to add this principle to your lunch bucket, add a class that creates ellipses. If you can do that, you can both understand the principle and apply it. That means it’s ready for work. Figure 3 shows the missing ellipse:

oc3
Figure 3: Test your understanding by adding an ellipse to the mix.

One caveat to using this principle is one of the first design pattern principles: favor composition over inheritance. That doesn’t mean you cannot create new elements for your program using extension—clearly some design patterns do so. Rather, just keep in mind that the principles in both OOP and design patterns work in conjunction with one another and you need to realize that not all OOP solutions are grounded in a single principle.

2 Responses to “Design Pattern Principles for ActionScript 3.0: The Open/Closed Principle”


  • Pardon my boner: when you declare IOpenClosed, are you using the “I” prefix to denote an Abstract class not meant to be directly instantiated? I was informed by assorted waggling fingers that the I-prefix was reserved for interfaces.

    That’s it for me, just a dumb question.

  • Hi Tf,

    Your point is a valid one, but I’ve decided that since Abstract classes are essentially referenced as an interface, that they too can be prefaced by an ‘I’, and that reminds me not to instantiate them. This particular Abstract class is pretty loaded up; and so it was doubly important not to forget.

    A point that I’ve come to understand is that “interface” is more important as a term denoting the methods, properties and signature of a class than the ActionScript statement “interface” used to denote those characteristics. When all you’re really using in an Abstract class is its interface (since you never instantiate it), it too is essentially an interface.

    Does that help explain it?

    Thanks for your comment, and no question is dumb except those that are unasked.

    Take care,
    Bill

Leave a Reply