Design Pattern Principles for ActionScript 3.0: Single Responsibility Principle

bucketruleThe final principle we’re going to examine for the Lunch Bucket Rules series is the Single Responsibility Principle. Succinctly stated, it means that each class should have one and only one responsibility. At the base of this principle is the idea that if you make a change, you are less likely to run into a problem if each class has a single responsibility. The same principle is expressed as, each class should only have a single reason to change.

In our book, we discuss this principle briefly (pg. 443). The MVC design represents three clear areas of responsibility—Model, View, and Controller. Each responsibility is clearly spelled out, and each of the three elements in the framework sticks with its own responsibility. That’s not a bad place to start as far as a getting a general sense of what this principle means.

Simple Never Is

In searching around for information about the Single Responsibility Principle, I came across a short post by David Chelimsky. In that post he cites the following quote:

In procedural programming, a process is expressed in one place in which a series of instructions are coded in order. Whereas in OO, a process is expressed as a succession of messages across objects. One object sends a message to another, which does part of the process and then sends a new message off to another object, which handles part of the process, etc. You modify a process by reorganizing the succession of messages rather than changing a procedure.

Ooof! That quote knocked the wind out of me because of all of its implications. It’s saying that when you make a change, your main focus is on rearranging responsibilities encapsulated in classes instead of re-writing a procedure. This forces the developer to ask, What responsibility does each class have? If each class has a single responsibility, that makes life a lot simpler. On the other hand, classes with multiple responsibilities may be ripped up or cause problems elsewhere because they can be changed in more ways than one and do more than one thing, wrecking unpredictable havoc.

What’s a Responsibility?

When we think of a class, the key elements that come to mind are properties and methods. A class with a single responsibility has a set of methods and properties to carry out that responsibility. The tough part comes when we expand our class to more than a single responsibility—unaware that we are doing so. In a lot of discussions the concept of a single responsibility becomes the main topic of debate. (Look around online and you’ll find plenty of examples.) What does a single responsibility mean? We know that it means a single reason to change and while that notion clarifies the issue, it still leaves much to be desired in nailing down responsibility.

For example, suppose you want to make a class that adds a text caption to a graphic on a page. You’ll probably have a method that returns the graphic and another one to return the caption. If we say that the class responsibility is to make a captioned graphic, we are talking about a single responsibility, no? The following example shows a program that creates the image in Figure 1.
sun
Figure 1: Captioned graphic

The program will include an abstract class with two abstract methods for creating the graphic (a shape) and a caption (text in a text field). The concrete class provides a detailed graphic and appropriate caption.

?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.text.TextField;
 
	//Abstract class
	public class Thing
	{
		protected var infoNow:String;
		protected var colorNow:uint;
		protected var anyShape:Shape;
		protected var anyText:TextField;
 
		public function someShape():Shape
		{
			//Abstract method
			return anyShape;
		}
 
		public function someCaption():TextField
		{
			//Abstract method
			return anyText;
		}
	}
}

The concrete class uses the parent class properties in creating a shape and a caption that goes with the shape. Its single responsibility is to make a “sun” graphic with a caption that lets the viewer know it’s a sun.

?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
27
28
//Concrete Class
package 
{
	import flash.display.Shape;
	import flash.text.TextField;
 
	public class ThingMaker extends Thing
	{
		override public function someShape():Shape
		{
			colorNow = 0xFF9900;
			anyShape=new Shape();
			anyShape.graphics.beginFill(colorNow,1 );
			anyShape.graphics.drawCircle(100,100,50);
			anyShape.graphics.endFill();
			return anyShape;
		}
 
		override public function someCaption():TextField
		{
			infoNow = "The Sun";
			anyText=new TextField();
			anyText.x = 75,anyText.y = 155;
			anyText.text = infoNow;
			return anyText;
		}
	}
}

So now, all the Client has to do is to request a shape and caption and display them. Developers can make as many different captioned graphics as they want by adding more concrete classes.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 
{
	import flash.display.Sprite;
 
	public class Client extends Sprite
	{
		private var thing:Thing=new ThingMaker();
 
		public function Client()
		{
			addChild(thing.someShape());
			addChild(thing.someCaption());
		}
	}
}

Now you may be tempted to ask, What about a caption-centering algorithm? or Couldn’t we add some parameters for flexibility?. For the time being, hold those thoughts so that we can focus on the concept of a single responsibility.

In looking at all of the literals used in the concrete class, you’re probably thinking, brittle. Also, you may be asking about other types of images that you want captioned, such as bitmapped graphics or images stored in SWF files. However, the question we need to ask is, does this design have more than one reason to change? Take a quick look at a class diagram of this program in Figure 2 to see what it can tell.
singlediagram
Figure 2: Single responsibility?

Immediately, you may suspect that you have detected two things that change in the class diagram. The shape and the caption can change. Put another way, the class has two responsibilities, creating a shape and creating a caption. The Client is responsible for displaying them, but the ThingMaker class has the dual responsibilities of creating a shape and caption.

Cohesion

In OOP programming, cohesion is another concept that we need to understand in relationship to the Single Responsibility Principle. In a programming context, cohesion refers to the extent to which classes are designed around related functions. In our example, we had one function that created shapes and another that created text fields. Because those two functions are inherently unrelated and in the same class, the class is considered to have low cohesion. We want classes with high cohesion, and so we tend to focus on single responsibilities within the class.

The problem in getting all of this right is that cohesion along with responsibility depends on context. If I use the context, captioned graphics both text fields and shape objects go together perfectly well. I could argue that the two methods have high cohesion because they are part of realizing a single responsibility. The responsibility is to create captioned graphics, and therefore, the two methods of generating a shape and a text field are inherently related.

Determining cohesion and what is and is not a single responsibility (or reason to change) is at the crux of the Single Responsibility Principle. The Freemans (Head First Design Patterns) make the same point. On the one hand, our minds are so facile that they automatically find relationships in problem solving resulting in classes with multiple responsibilities. On the other hand, we cannot solve the single responsibility problem by making it a single method or property issue. At some level, we have to know our programming language intimately and artfully and what elements go together, while never infallible, gets clearer with usage.

Getting Granular

Looking at the simple example we have, it should be pretty clear that you might want to use a captioning class for a variety of different graphics and not just shapes. However, we’ve joined the caption with the shape, and so if you get one (the caption) you get the other (the shape.) That makes for a not-too-flexible class. So, to loosen things up, let’s create separate abstract classes for each of the responsibilities. Then, from those interfaces, we can have specific tasks. For example, we’ll expand the graphic classes to include both round and rectangular shapes. Further, we’ll include both captions and body text to a text maker class. Additionally, we can add format methods to both of the text subclasses. Download Files Here . Figure 3 shows a more granular set of classes:
singlediagram2
Figure 3: Single responsibilities distributed to components

In looking at Figure 3, you can see that the two abstract classes have a more focused responsibility. However, even with a single responsibility in the parent classes, you can still have a wide variety of implementations of the single responsibilities giving the design far more flexibility.

To see how the more focused but still broad components in the design work, the Client invokes two of the TextWork subclasses to give the image both a caption and a little write-up. Figure 4 shows the results:
mars
Figure 4: Granularity adds flexibility

Adding a formatting function to the implementations of the text handling subclasses allows better control over captions and body text. The single responsibility is to return a text field with formatted text, and in this context, changing or adding formatting elements will not break the design.

First, instead of having a single class with two responsibilities, this design begins with two abstract classes, each with a single responsibility. Second, by separating the graphic element from the text element, the design has greater flexibility. To demonstrate this flexibility, the design has two concrete classes for each of the parent classes. The following classes make up the new design:

Abstract class for shapes.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package 
{
	import flash.display.Shape;
 
	//Abstract class
	public class ShapeWork
	{
		protected var colorNow:uint;
		protected var anyShape:Shape;
 
		public function someShape():Shape
		{
			//Abstract method
			return anyShape;
		}
	}
}

The first ShapeWork subclass creates circles.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package 
{
	import flash.display.Shape;
 
	//Concrete class
	public class CircleShape extends ShapeWork
	{
		public override function someShape():Shape
		{
			colorNow = 0xD95A2B;
			anyShape=new Shape();
			anyShape.graphics.beginFill(colorNow,1 );
			anyShape.graphics.drawCircle(125,100,50);
			anyShape.graphics.endFill();
			return anyShape;
		}
	}
}

The second ShapeWork subclass creates rectangles.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package 
{
	import flash.display.Shape;
 
	//Concrete class
	public class RectangleShape extends ShapeWork
	{
		public override function someShape():Shape
		{
			colorNow = 0xCF573D;
			anyShape=new Shape();
			anyShape.graphics.beginFill(colorNow,1 );
			anyShape.graphics.drawRect(0,0,100,75);
			anyShape.graphics.endFill();
			return anyShape;
		}
	}
}

Next, the TextWork abstract class sets up the child classes for both text fields and text formatting. The single responsibility is to make formatted text fields. The class contains three properties, one each for a String, a TextField and a TextFormat object.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 
{
	//Abstract Class
	import flash.text.TextField;
	import flash.text.TextFormat;
 
	public class TextWork
	{
		protected var infoNow:String;
		protected var anyText:TextField;
		protected var infoFormat:TextFormat;
 
		public function someText():TextField
		{
			//Abstract method
			return anyText;
		}
	}
}

First, the CaptionMaker is able to specify both a single line field and format it with a big bold font (Arial Black). It specializes in captions.

?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
27
28
29
30
31
package 
{
	//Concrete Class
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
 
	public class CaptionMaker extends TextWork
	{
 
		public override function someText():TextField
		{
			infoNow = "Planet Mars";
			anyText=new TextField();
			setFormat();
			anyText.x = 75,anyText.y = 155;
			anyText.text = infoNow;
			anyText.autoSize = TextFieldAutoSize.CENTER;
			return anyText;
		}
 
		private function setFormat():void
		{
			infoFormat=new TextFormat();
			infoFormat.font = "Arial Black";
			infoFormat.size = 13;
			infoFormat.color = 0x33160F;
			anyText.defaultTextFormat = infoFormat;
		}
	}
}

To broaden the utility of the available text objects, the second text child class is used to make body text. Using a small, sans serif font (11 point Verdana) a highly readable body provides the details of an image. (Normally, you would probably use a loader routine to bring in text stored in a text file, but for this example we have to resort to a big string to keep the focus on the Single Responsibility Principle.)

?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
27
28
29
30
31
package 
{
	//Concrete Class
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
 
	public class BodyMaker extends TextWork
	{
		public override function someText():TextField
		{
			infoNow = "Here's a lot of information about Mars. To begin with it's red very much like Georgia clay. Evidence of water has been found beneath the surface by the Mars Rover.";
			anyText=new TextField();
			setFormat();
			anyText.width = 250;
			anyText.multiline = true,anyText.wordWrap = true;
			anyText.x = 80,anyText.y = 175;
			anyText.text = infoNow;
			anyText.autoSize = TextFieldAutoSize.LEFT;
			return anyText;
		}
 
		private function setFormat():void
		{
			infoFormat=new TextFormat();
			infoFormat.font = "Verdana";
			infoFormat.size = 11;
			anyText.defaultTextFormat = infoFormat;
		}
	}
}

Finally, we can use the Client to request an image with both a caption and a write up. As you can see, the calls were simple, and if any changes were made to any of the called components, it wouldn’t affect the other aspects of the program.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package 
{
	import flash.display.Sprite;
 
	public class Client extends Sprite
	{
		private var bodyTxt:TextWork=new BodyMaker();
		private var caption:TextWork=new CaptionMaker();
		private var mars:ShapeWork=new CircleShape();
 
		public function Client()
		{
			addChild(mars.someShape());
			addChild(caption.someText());
			addChild(bodyTxt.someText());
		}
	}
}

You can review the single responsibilities clearly in Figure 4. In fact, each of the display elements shows a single responsibility in each of the classes.

Into the Lunch Bucket

First things first. Don’t get sidetracked by the philosophical issue of, What is a single responsibility? Just keep in mind that your class should have only a single responsibility or reason to change.

Some developers are put off by several responsibilities being farmed out to different classes. Why not keep things simple and bundle responsibilities into a single class? Remember, we’re not trying to save silicon! We’re trying to create flexible, reusable code. So even though you may be able to cram everything into a single class (or even a single line) doesn’t mean it’s a good idea to do so. The more responsibilities in a class, the less flexible it is, and the more likely it will crash and burn when change is introduced.

Second, the single responsibility principle is widely used in virtually every design pattern. If you’re not sure whether your class has more than a single reason to change, take a look at some design patterns. They’ll help you keep your code flexible and reusable by limiting the responsibilities the classes have.

By all means take this principle to work, and remember what we first noted at the beginning of this post:

You modify a process by reorganizing the succession of messages rather than changing a procedure.

With that tucked into your lunch bucket, you can’t go wrong.

  • Share/Bookmark

Related posts:

  1. Design Pattern Principles for ActionScript 3.0: The Open/Closed Principle
  2. Design Pattern Principles for ActionScript 3.0: The Liskov Substitution Principle
  3. Design Pattern Principles for ActionScript 3.0: The Dependency Inversion Principle

6 Responses to “Design Pattern Principles for ActionScript 3.0: Single Responsibility Principle”


  • Seriously, no comments yet? I found this article fantastic as it’s a principle that I preach about anytime I have an excuse to. All too often, Flash developers wrap way too much functionality and responsibility into a single class that’s hundreds of lines long. Building projects in a modular way under the single responsibility principle can totally save a project from ramping into unmanageable complexity.

  • Hey Jonathan,

    It just went up this morning; so it’ll take a while for folks on this list to respond. So many people just develop with a single giant class because that’s what they’re used to and it feels safe. I hope that little by little people try programming with some of these principles–they really do work! When I was developing examples for this post, it was super easy to make changes.

    As an alternative to either a Flowchart or a Class Diagram, maybe a “Responsibility Chart” would be the way to develop!

    Kindest regards,
    Bill

  • Hello,

    very great to encapsule the code, or change it after.

  • Hi Ramon,

    Remember to separate what varies from what stays the same and encapsulate what varies.

    Take care,
    Bill

  • I’d just like to add that quite often I don’t completely encapsulate from the get go, but often clean up my code.
    For example when writing a flash game I may add the code for something, get it working, and then go back and clean it up before I move onto the next feature.

    I personally don’t think many people out there can code in such an encapsulated manor straight off the bat. It would require a very seasoned coder who can churn out flawless UML.

  • Hi Justin,

    UMLs were developed as architectural tools for Object Oriented Design (OOD). Like a blueprint for building a house, a UML diagram is an aid and nothing more. The more detailed a UML, the easier it is to build a program because it tells you what goes where. So, a rudimentary UML is better than none at all. (The ones I sketch on the back of used daily calendar pages are definitely rudimentary.)

    You also brought up our old friend perfection in reference to flawless UMLs. Remember that seeking perfection is like going after the Holy Grail—an impossible and even foolhardy task because you’ll never achieve it. On the other hand, excellence is a process whereby you seek out improvement incrementally. The more you do it, the better you get at it. (That’s why I’m using discarded calendar pages instead of pulling brand new paper out of my laster printer! I need to get better at the process first.)

    Working out an operation prior to encapsulating it is hardly a sin! However, encapsulating operations is the essence of OOP. Further, unit-testing is another essential step in building a real-world application, and so as a developer builds a program, he wants to make sure that all of the pieces (objects) are working prior to putting them all together. So it seems to me that you’re involved in a type of unit-testing prior to encapsulation. In game development (as you well know) virtually all objects interact with other objects. As a result, you probably need to know which objects should be unit-tested with other objects and see if the objects work as expected and interact as expected. To interact, they need some form of encapsulation so that they are tested as objects and not solely as operations. Your technique seems to involve an extra step, but there’s nothing wrong with that. As you get better, you won’t have to work as hard!

    Kindest regards,
    Bill

Leave a Reply