Home > Chain of Responsibility, JavaScript > From ActionScript 3.0 to JavaScript Chain of Responsibility: Part I

From ActionScript 3.0 to JavaScript Chain of Responsibility: Part I

After Mom, Ask Dad, and then the Dog

After Mom, Ask Dad, and then the Dog

Just Follow the Chain of Responsibility

To get started on the quest to see whether useful design patterns can be created with JavaScript, I thought it’d be a good idea to go from the known to the unknown. In this case, those familiar with this blog know ActionScript 3.0 design patterns, and the unknown (for some at least) is JavaScript. Well, I suppose just about everyone who reads this blog knows something about JavaScript, but several may not have been involved with either JavaScript OOP or design patterns. By creating an ActionScript 3.0 app using a pattern and then going through the app step-by-step, readers may be better able to understand how we might approach design patterns using JavaScript. I decided to use the Chain of Responsibility (CoR) pattern to go through the types of mobile operating systems that we examined in the previous post. In this way, we can create something both familiar and practical.

Every time I use the Chain of Responsibility pattern, I feel that I’m swatting a fly with a weapon of mass destruction. I see all of these classes, and I’m thinking that I could have done the same thing with a switch statement or something even slicker. Then I need to remind myself why I’d use the CoR in the first place.

The Chain of Responsibility separates the handling of an event from the request to handle it.

In a broader context, this allows the developer to make changes to either the request or the handling of the event without disrupting some other part of the program. It’s quite simple as well. Just imagine several people lined up with different kinds of expertise and/or authority. A request is issued, and once it reaches the person with the right expertise or authority, it is handled. The request is separated from the way it will be handled. That’s up to the expert. Figure 1 shows the class design:

<em><strong>Figure 1: </strong> Chain of Responsibility Pattern</em>

Figure 1: Chain of Responsibility Pattern

Getting Started with CoR

For details about ways to setup and use CoR see our original discussion . The focal points are in the following:

  • An interface (an abstract class or interface) establishes a handler operation
  • Each request handler has a separate class derived from the handler interface
  • Each handler class has a successor which is another handler class
  • The last handler in the chain has no successor
  • As soon as a handler can handle the request, the chain stops and the handler takes care of the request.

You may be thinking that this is not very efficient since it requires a sequential path. We’ve considered ways to get around a sequence (see our posts on Skip Lists), but for now we’ll stick with the traditional CoR and use a sequential search through the list of handlers.

The Chain of Mobile Operating Systems

This CoR simulates finding one of several types of mobile OS. Given the type of OS, it looks for ways to handle each one optimally. In the JavaScript version of this, we want to have it select a CSS file or another JS file to best use the kind of mobile device involved. The concrete handler classes are the objects used to deal with whatever requirements are necessary. In the example, each handler simply traces out “Set up for xxOS,” where “xx” is the found mobile OS. It also traces out which handlers were rejected so that you can better see how the chain works. Since Android is at the top of the chain, you see no rejections, while Windows CE at the bottom of the chain displays the whole chain.

It might be helpful if you take a look at the whole concept of Linked Lists. The Chain of Responsibility is something like a linked list, but instead of linking lists, it links handlers.

The Client class makes the initial request and sets up a simple requesting UI for testing the application. It makes requests using a string. I just used the lowercase ID of the main mobile OS types as the “request-to-handle.” After making the request, the “chain” takes over. In this example the top of the chain is the Android, but it could be any of the others as well. The developer has complete control over the sequence.

?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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package
{
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import fl.controls.Button;
	import fl.controls.TextInput;
	import fl.controls.Label;
	import flash.text.TextFieldAutoSize;
 
	public class Client extends Sprite
	{
		//Handlers + Request
		private var android:Handler= new Android();
		private var iphone:Handler= new IPhone();
		private var blackberry:Handler= new Blackberry();
		private var series60:Handler=new Series60();
		private var windowsce:Handler=new WindowsCE();
		private var cannothandle:Handler=new CannotHandle();
 
		//UI
		private var btn:Button=new Button();
		private var iput:TextInput=new TextInput();
		private var lbl:Label=new Label();
 
		public function Client()
		{
			setupChain();
			setupUI();
		}
 
		private function setupChain():void
		{
			//Sequence set up here
			android.SetSuccessor(iphone);
			iphone.SetSuccessor(blackberry);
			blackberry.SetSuccessor(series60);
			series60.SetSuccessor(windowsce);
			windowsce.SetSuccessor(cannothandle);
		}
 
		private function setupUI():void
		{
			//This simulates getting the mobile OS from the using system
			lbl.autoSize = TextFieldAutoSize.LEFT;
			lbl.text="Enter name of mobile device: (Use lower case.)";
			lbl.x=50,lbl.y=30;
			addChild(lbl);
			btn.x=50, btn.y=75;
			btn.label="Start the chain";
			btn.addEventListener(MouseEvent.CLICK,requestOS);
			addChild(btn);
			iput.x=50,iput.y=50;
			addChild(iput);
		}
 
		private function requestOS(e:MouseEvent)
		{
			android.HandleRequest(iput.text);
		}
	}
}

For the end of the chain, I added a “CannotHandle” class that extends the Handler interface. This functions something like a default option in a switch statement. If none of the handlers can handle the request, you need to provide the user with some kind of feedback.

The Abstract and Concrete Handlers

In this application the interface is an abstract class, named Handler. The abstract method, HandleRequest() takes a string argument and checks if it’s the appropriate class to handle the request, and if not, passes it on to the successor. The last concrete Handler object is always the CannotHandle class.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package
{
	//Abstract class
 
	public class Handler
	{
		protected var successor:Handler;
		protected var mobileType:String;
 
		public function SetSuccessor (successor:Handler):void
		{
			this.successor=successor;
		}
 
		public function HandleRequest (req:String):void
		{
			//Provide details in subclasses
		}
	}
}

With ActionScript 3.0, you have to make up abstract classes by including at least one method that you want to be abstract and must be overridden when implemented. In this case the HandleRequest(reg:String) method expects to be overridden. On the other hand, the SetSuccessor method that expects a concrete handler as an argument is inherited all set to use. Just add an argument. In deciding where to implement the SetSuccessor method, I decided to use the Client. In this way the developer can clearly see the order of the chain. Further, if changes are made, the CannotHandle class can easily be moved to the end of the chain without having to change the sequencing within the concrete handler classes.

The two handler properties, successor and mobileType are used to identify the successor class (object) and store the name of the operating system, respectively. All that’s left is to provide the concrete implementations which involves two steps:

  1. Overidding the HandleRequest() method and adding whatever you want the class to do if it’s to handle the request.
  2. Sending the search off to the successor if it cannot handle the request.

That’s not exactly rocket science. This is a very easy design pattern to implement. I added a private method to each class, sendBack() that represents a more complex handling of any request outside of the conditional statement that identifies the handler as the appropriate one for the request. The more differentiated and complex the handlers, the more that you can appreciate the CoR. You can add anything you need to handle a request without having to worry about the requester (Client) or the other ways available to handle a request. Further, when you add or change a handler to the chain, it will not disrupt the rest of the program.

The following six classes represent concrete handlers. All of them are almost identical other than having different mobile OS’s to sort out. The CannotHandle concrete handler is just a caboose the both mark the end of the chain and let the user know that his request cannot be handled. It has no successor and the sendBack() function tells the user that her request cannot be handled.

?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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//Android
package
{
	public class Android extends Handler
	{
		//Concrete handler
		override public function HandleRequest(req:String):void
		{
			if (req =="android")
			{
				mobileType = "Now setting up " + req + " environment.";
				sendBack();
			}
			else if (successor != null)
			{
				trace("Not Android");
				successor.HandleRequest(req);
			}
		}
 
		private function sendBack():void
		{
			trace(mobileType);
		}
	}
}
 
//iPhone
package
{
	public class IPhone extends Handler
	{
		//Concrete handler
		override public function HandleRequest(req:String):void
		{
			if (req =="iphone")
			{
				mobileType = "Now setting up " + req + " environment.";
				sendBack();
			}
			else if (successor != null)
			{
				trace("Not iPhone");
				successor.HandleRequest(req);
			}
		}
 
		private function sendBack():void
		{
			trace(mobileType);
		}
	}
}
 
//BlackBerry
package
{
	public class Blackberry extends Handler
	{
		//Concrete handler
		override public function HandleRequest(req:String):void
		{
			if (req =="blackberry")
			{
				mobileType = "Now setting up " + req + " environment.";
				sendBack();
			}
			else if (successor != null)
			{
				trace("Not Blackberry");
				successor.HandleRequest(req);
			}
		}
 
		private function sendBack():void
		{
			trace(mobileType);
		}
	}
}
 
//Series60
package
{
	public class Series60 extends Handler
	{
		//Concrete handler
		override public function HandleRequest(req:String):void
		{
			if (req =="series60")
			{
				mobileType = "Now setting up " + req + " environment.";
				sendBack();
			}
			else if (successor != null)
			{
				trace("Not Series60");
				successor.HandleRequest(req);
			}
		}
 
		private function sendBack():void
		{
			trace(mobileType);
		}
	}
}
 
//Windows CE
package
{
	public class WindowsCE extends Handler
	{
		//Concrete handler
		override public function HandleRequest(req:String):void
		{
			if (req =="windowsce")
			{
				mobileType = "Now setting up " + req + " environment.";
				sendBack();
			}
			else if (successor != null)
			{
				trace("Not WindowsCE");
				successor.HandleRequest(req);
			}
		}
 
		private function sendBack():void
		{
			trace(mobileType);
		}
	}
}
 
//CannotHandle
package
{
	public class CannotHandle extends Handler
	{
		//Concrete handler
		override public function HandleRequest(req:String):void
		{
			mobileType = "Rats! We looked and couldn't find that mobile OS. \n"
			mobileType += "We'll just have to get to work and see if we can \ndevelop a way to handle it for you.";
			sendBack();
 
		}
		private function sendBack():void
		{
			trace(mobileType);
		}
	}
}

Testing the Chain

<em><strong>Figure 2: </strong> UI provides a way to test the chain.</em>

Figure 2: UI provides a way to test the chain.

When all is said and done, this pattern is pretty simple. Essentially the Client makes a request through the Handler interface and one of the concrete handlers deals with the request. If it cannot deal with it, it sends it along the chain until one of the handlers can or report that it cannot handle the request. The UI in this example (see Figure 2) simulates a JavaScript method for finding the current user’s mobile OS.

If an OS near the end of the chain is selected, you can see all of the links in the chain used to find the correct handler. Figure 3 shows what occurs if “windowsce” is requested. Had “android” been the search OS, it would have popped up without any links in the chain showing it was not found.

<em><strong>Figure 3: </strong> Output showing steps in sequence and feedback.</em>

Figure 3: Output showing steps in sequence and feedback.

Finally, if the request cannot be handled, an important development step is to add a handler that tells the user that his OS cannot be found. The final handler in a chain has no successor, and you definitely do not want to link back to the beginning of the chain! That would set up an infinite loop. Figure 4 shows the feedback when a request cannot be handled:

<em><strong>Figure 4: </strong> Feedback for an unhandled request.</em>

Figure 4: Feedback for an unhandled request.

JavaScript is the Next Link

In Part II, I’d like to step through these same classes using JavaScript prototype classes and see if we can use the same pattern. I think that if each concrete handler can configure the screen for a particular mobile device, it would be quite useful. As new devices are added, such as iPad, Kindle Fire, and various other tablets, you will find that the flexibility afforded by the Chain of Responsibility. To get ready for Part II, go over the ActionScript 3.0 version and make sure everything makes sense. Send in comments if you have a question.

Share

Related posts:

  1. PHP Chain of Responsibility Design Pattern from ActionScript 3.0
  2. ActionScript 3.0 Chain of Responsibility Design Pattern: Decoupling Request and Request Handler
  3. Can JavaScript Create Design Patterns?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>