Archive for April, 2010

“Type(var)” vs “var as Type”

Friday, April 23rd, 2010

Description:
I have done an experiment to test the performance of data type conversions in actionscript 3. Create a new flex 4 based application with the code below, then compile and run it:


CODE:

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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx"
			   minWidth="955" minHeight="600"
			   xmlns:controls="com.wds.framework.controls.*"
			   initialize="application1_initializeHandler(event)">
	<fx:Script>
		<![CDATA[
			import mx.containers.Canvas;
			import mx.core.Container;
			import mx.core.FlexSprite;
			import mx.core.IContainer;
			import mx.core.IUIComponent;
			import mx.core.UIComponent;
			import mx.events.FlexEvent;
			import mx.utils.ObjectProxy;
 
			protected function application1_initializeHandler(event:FlexEvent):void
			{
				var n:int = 1000000;
				var i:int = 0;
				var timer:int = 0;
				var obj:*;
 
				trace("--------------------------");
				////////////////////////////////////////////////////
				trace("Basic Types");
				trace("-----------");
				obj = {};
				trace("obj = {}");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Object(obj);
				}
				trace("Object(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Object;
				}
				trace("obj as Object",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = "str";
				trace("obj = \"str\"");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					String(obj);
				}
				trace("String(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as String;
				}
				trace("obj as String",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = 0.5;
				trace("obj = 0.5");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Number(obj);
				}
				trace("Number(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Number;
				}
				trace("obj as Number",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = -12;
				trace("obj = -12");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					int(obj);
				}
				trace("int(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as int;
				}
				trace("obj as int",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = 12;
				trace("obj = 12");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					uint(obj);
				}
				trace("uint(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as uint;
				}
				trace("obj as uint",getTimer() - timer);
				trace("-----------");
 
				////////////////////////////////////////////////////
				trace("Dynamic Classes");
				trace("-----------");
				obj = new Array();
				trace("obj = new Array()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Array(obj);
				}
				trace("Array(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Array;
				}
				trace("obj as Array",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new ObjectProxy();
				trace("obj = new ObjectProxy()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					ObjectProxy(obj);
				}
				trace("ObjectProxy(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as ObjectProxy;
				}
				trace("obj as ObjectProxy",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new MyDynamicClass();
				trace("obj = new MyDynamicClass()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					MyDynamicClass(obj);
				}
				trace("MyDynamicClass(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as MyDynamicClass;
				}
				trace("obj as MyDynamicClass",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new MyProxyClass();
				trace("obj = new MyProxyClass()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					MyProxyClass(obj);
				}
				trace("MyProxyClass(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as MyProxyClass;
				}
				trace("obj as MyProxyClass",getTimer() - timer);
				trace("-----------");
				////////////////////////////////////////////////////
				trace("Typed Classes");
				trace("-----------");
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Canvas(obj);
				}
				trace("Canvas(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Canvas;
				}
				trace("obj as Canvas",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Container(obj);
				}
				trace("Container(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Container;
				}
				trace("obj as Container",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					UIComponent(obj);
				}
				trace("UIComponent(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as UIComponent;
				}
				trace("obj as UIComponent",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Sprite(obj);
				}
				trace("Sprite(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Sprite;
				}
				trace("obj as Sprite",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					DisplayObject(obj);
				}
				trace("DisplayObject(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as DisplayObject;
				}
				trace("obj as DisplayObject",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					EventDispatcher(obj);
				}
				trace("EventDispatcher(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as EventDispatcher;
				}
				trace("obj as EventDispatcher",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Object(obj);
				}
				trace("Object(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Object;
				}
				trace("obj as Object",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Container();
				trace("obj = new Container()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Container(obj);
				}
				trace("Container(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Container;
				}
				trace("obj as Container",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new UIComponent();
				trace("obj = new UIComponent()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					UIComponent(obj);
				}
				trace("UIComponent(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as UIComponent;
				}
				trace("obj as UIComponent",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new FlexSprite();
				trace("obj = new FlexSprite()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					FlexSprite(obj);
				}
				trace("FlexSprite(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as FlexSprite;
				}
				trace("obj as FlexSprite",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Sprite();
				trace("obj = new Sprite()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					Sprite(obj);
				}
				trace("Sprite(obj)",getTimer() - timer);
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as Sprite;
				}
				trace("obj as Sprite",getTimer() - timer);
				trace("-----------");
 
				////////////////////////////////////////////////////
				trace("Interfaces");
				trace("-----------");
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					IContainer(obj);
				}
				trace("IContainer(obj)",getTimer() - timer);				
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as IContainer;
				}
				trace("obj as IContainer",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					IUIComponent(obj);
				}
				trace("IUIComponent(obj)",getTimer() - timer);				
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as IUIComponent;
				}
				trace("obj as IUIComponent",getTimer() - timer);
				trace();
 
				//////////////////////////
				obj = new Canvas();
				trace("obj = new Canvas()");
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					IEventDispatcher(obj);
				}
				trace("IEventDispatcher(obj)",getTimer() - timer);				
 
				timer = getTimer();
				for(i = 0;i < n;i++)
				{
					obj as IEventDispatcher;
				}
				trace("obj as IEventDispatcher",getTimer() - timer);
				trace();
			}
 
		]]>
	</fx:Script>
</s:Application>

RESULTS:

--------------------------
Basic Types
-----------
obj = {}
Object(obj) 150
obj as Object 109

obj = "str"
String(obj) 110
obj as String 110

obj = 0.5
Number(obj) 65
obj as Number 107

obj = -12
int(obj) 67
obj as int 110

obj = 12
uint(obj) 67
obj as uint 108
-----------
Dynamic Classes
-----------
obj = new Array()
Array(obj) 1563
obj as Array 113

obj = new ObjectProxy()
ObjectProxy(obj) 126
obj as ObjectProxy 134

obj = new MyDynamicClass()
MyDynamicClass(obj) 128
obj as MyDynamicClass 138

obj = new MyProxyClass()
MyProxyClass(obj) 133
obj as MyProxyClass 135
-----------
Typed Classes
-----------
obj = new Canvas()
Canvas(obj) 131
obj as Canvas 132

obj = new Canvas()
Container(obj) 136
obj as Container 144

obj = new Canvas()
UIComponent(obj) 141
obj as UIComponent 148

obj = new Canvas()
Sprite(obj) 176
obj as Sprite 124

obj = new Canvas()
DisplayObject(obj) 167
obj as DisplayObject 122

obj = new Canvas()
EventDispatcher(obj) 170
obj as EventDispatcher 125

obj = new Canvas()
Object(obj) 150
obj as Object 108

obj = new Container()
Container(obj) 132
obj as Container 138

obj = new UIComponent()
UIComponent(obj) 134
obj as UIComponent 138

obj = new FlexSprite()
FlexSprite(obj) 133
obj as FlexSprite 134

obj = new Sprite()
Sprite(obj) 170
obj as Sprite 120
-----------
Interfaces
-----------
obj = new Canvas()
IContainer(obj) 136
obj as IContainer 147

obj = new Canvas()
IUIComponent(obj) 139
obj as IUIComponent 148

obj = new Canvas()
IEventDispatcher(obj) 120
obj as IEventDispatcher 121

Conclusion

  • To convert an Object instance to String, uint, int or Number, “Type(var)” is faster than “var as Type”
  • To convert an Object  instance to Array,  ”Type(var)” is much slower than “var as Type”, you can receive a warning “Array(x) behaves the same as new Array(x). To cast a value to type Array use the expression x as Array instead of Array(x)” with Flash Builder 4.
  • To convert an Object  instance of a sub-class to its super class, “var as Type” is faster than “Type(var)” if the super class is not Sprite or its super classes.
  • To convert an Object instance of a class to that class instance, ”var as Type” is faster than “Type(var)” sometimes if the super class is not Sprite or its super classes.
  • To convert an Object instance of a class as an interface, ”Type(var)” is faster than “var as Type”.

as3 DateParser

Friday, April 23rd, 2010
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
////////////////////////////////////////////////////////////////////////////////
// DateParser.as
//
// An as3 implementation of the java SimpleDateFormat like class based on Flex 
// SDK which can be used to parse a String with specified format to as3 Date 
// object.
//
// This code is a modification version of Daniel Wabyick's as2 
// SimpleDateFormatter(http://osflash.org/simpledateformatter) which 
// is directly adapted from Matt Kruse's Javascript class implementation.
//
// @author edison.guo(http://www.hydra1983.com)
// @author Daniel Wabyick(http://www.fluid.com)
// @author Matt Kruse (http://www.JavascriptToolbox.com)
 
// The following notice is maintained from Matt Kruse's 
// original Javascript code. 
// 
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// USAGE
// ----------------------------------------------------------------------
// These functions use the same pattern letters used by the DateFormatter 
// class provided by Flex SDK. You can find the detailed description of 
// these letter in the Adobe Flex Language Reference.
// Examples:
//		var dateString:String = "9/10/2008 GMT+0800 08:30:41";
// 		var parser:DateParser = new DateParser();
//		parser.formatString = "M/D/Y HH:NN:SS";
//		trace(parser.parse(dateString));
//		//Wed Sep 10 08:30:41 GMT+0800 2008
//
// ----------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
 
package
{
	import mx.formatters.Formatter;
	import mx.utils.StringUtil;
 
	public class DateParser
	{
		private static const VALID_PATTERN_CHARS:String = "Y,M,D,A,E,H,J,K,L,N,S,Q";
 
		public var error:String;
 
		private var _formatString:String = "YYYY-MM-DD HH:NN";
 
		public function get formatString():String
		{
			return _formatString;
		}
 
		public function set formatString(value:String):void
		{
			if(value) _formatString = value;
		}
 
		private var _defaultInvalidFormatError:String = Formatter.defaultInvalidFormatError;
 
		public function get defaultInvalidFormatError():String
		{
			return _defaultInvalidFormatError;
		}
 
		public function set defaultInvalidFormatError(value:String):void
		{
			_defaultInvalidFormatError = value;
		}
 
		private var _defaultInvalidValueError:String = Formatter.defaultInvalidValueError;
 
		public function get defaultInvalidValueError():String
		{
			return _defaultInvalidValueError;
		}
 
		public function set defaultInvalidValueError(value:String):void
		{
			_defaultInvalidValueError = value;
		}
 
		public function parse( value:Object):Date 
		{
			if (error)
				error = null;
 
			if(!(value is String))
			{
				error = defaultInvalidValueError;
				return null;
			}
 
			if(!value || value == "")
			{
				error = defaultInvalidValueError;
				return null;
			}
 
			var timezoneRegEx:RegExp = /(GMT|UTC)((\+|-)\d\d\d\d(\s)?)?/ig;
			value = StringUtil.trim(String(value).replace(timezoneRegEx, ""));
 
			var letter:String;
			var nTokens:int = 0;
			var tokens:String = "";
 
			var n:int = formatString.length;
			for (var i:int = 0; i < n; i++)
			{
				letter = formatString.charAt(i);
				if (VALID_PATTERN_CHARS.indexOf(letter) != -1 && letter != ",")
				{
					nTokens++;
					if (tokens.indexOf(letter) == -1)
					{
						tokens += letter;
					}
					else
					{
						if (letter != formatString.charAt(Math.max(i - 1, 0)))
						{
							error = defaultInvalidFormatError;
							return null;
						}
					}
				}
			}
 
			if (nTokens < 1)
			{
				error = defaultInvalidFormatError;
				return null;
			}
 
			var dataParser:DateStringParser = new DateStringParser(
				formatString, VALID_PATTERN_CHARS);
 
			return dataParser.parseValue(value);	
		}
	}
}
import mx.formatters.DateBase;
 
class DateStringParser
{
	private static var digits:String = "1234567890";
 
	private static var dayNames:Array = DateBase.dayNamesLong.concat(DateBase.dayNamesShort);
 
	private static var monthNames:Array = DateBase.monthNamesLong.concat(DateBase.monthNamesShort);
 
	public function DateStringParser(format:String, tokens:String)
	{
		super();
 
		this.format = format;
		this.tokens = tokens;
	}
 
	private var format:String;
 
	private var tokens:String;
 
	public function parseValue(value:Object):Date
	{		
		var valueString:String = String(value);
 
		var i_value:int = 0;
		var i_format:int = 0;
		var char_token:String = "";
		var token:String = "";
		var start:int = 0;
		var end:int = 0;
 
		var now:Date = new Date();
		var years:Object = now.getFullYear();
		var months:Object = now.getMonth() + 1;
		var dates:Object = now.getDate();	
		var days:Object = now.getDay();
		var hours:Object = now.getHours();
		var minutes:Object = now.getMinutes();
		var seconds:Object = now.getSeconds();
		var milliseconds:Object = now.getMilliseconds();
		var ampm:String = "";
 
		var max:int = 0;
		var min:int = 0;
 
		while(i_format < format.length)
		{
			char_token = format.charAt(i_format);
			if(tokens.indexOf(char_token) > -1)
			{
				start = format.indexOf(char_token);
				end = format.lastIndexOf(char_token);
				end = end >= 0 ? end + 1 : start + 1;
				token = format.substring(start,end);			
				i_format = end;
 
				if(token == "YYYY" || token == "YY" || token == "Y")
				{					
					if(token == "YYYY") {max = min = 4;}
					if(token == "YY") {max = min = 2;}
					if(token == "Y"){max = 4;min = 2;}
 
					years = getIntegerValue(valueString,i_value,min,max);
					if(isNaN(Number(years))) return null;
					i_value += String(years).length;
					years = Number(years);
				}
				else if(token == "MM" || token == "M")
				{
					months = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(months)) || months < 1 || months > 12) return null;
					i_value += String(months).length;
					months = Number(months);
				}
				else if(token == "MMMM" || token == "MMM")
				{
					var monthName:String = getMonthName(valueString,i_value);
					if(!monthName || monthName.length == 0) return null;
					months = getMonths(monthName);
					if(isNaN(Number(months)) || months < 1 || months > 12) return null;
					i_value += monthName.length;
					months = Number(months);
				}
				else if(token == "EE" || token == "E")
				{
					days = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(days)) || days < 1 || days > 7) return null;
					i_value += String(days).length;
					days = Number(days);
				}
				else if(token == "EEEE" || token == "EEE")
				{
					var dayName:String = getDayName(valueString,i_value);
					if(!dayName || dayName.length == 0) return null;
					days = getDays(dayName);
					if(isNaN(Number(days)) || days < 1 || days > 7) return null;
					i_value += dayName.length;
					days = Number(days);
				}
				else if(token == "DD" || token == "D")
				{
					dates = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(dates)) || dates < 1 || dates > 31) return null;
					i_value += String(dates).length;
					dates = Number(dates);
				}
				else if(token == "AA" || token == "A")
				{
					ampm = valueString.substr(i_value,2).toUpperCase();
					if(ampm != "AM" && ampm != "PM") return null;
					i_value += 2;
				}
				else if(token == "JJ" || token == "J")
				{
					hours = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(hours)) || hours < 0 || hours > 23) return null;
					i_value += String(hours).length;
					hours = Number(hours);
				}
				else if(token == "HH" || token == "H")
				{
					hours = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(hours)) || hours < 1 || hours > 24) return null;
					i_value += String(hours).length;
					hours = Number(hours);
				}				
				else if(token == "KK" || token == "K")
				{
					hours = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(hours)) || hours < 0 || hours > 11) return null;
					i_value += String(hours).length;
					hours = Number(hours);
				}
				else if(token == "LL" || token == "L")
				{
					hours = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(hours)) || hours < 1 || hours > 12) return null;
					i_value += String(hours).length;
					hours = Number(hours);
				}
				else if(token == "NN" || token == "N")
				{
					minutes = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(minutes)) || minutes < 0 || minutes > 59) return null;
					i_value += String(minutes).length;
					minutes = Number(minutes);
				}
				else if(token == "SS" || token == "S")
				{
					seconds = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(seconds)) || seconds < 0 || seconds > 59) return null;
					i_value += String(seconds).length;
					seconds = Number(seconds);
				}
				else if(token == "QQQ" || token == "QQ" || token == "Q")
				{
					milliseconds = getIntegerValue(valueString,i_value,token.length,2);
					if(isNaN(Number(milliseconds)) || milliseconds < 0 || milliseconds > 999) return null;
					i_value += String(milliseconds).length;
					milliseconds = Number(milliseconds);
				}
			}
			else
			{
				i_format ++;
				i_value ++;
			}	
		}
 
		if (i_value != valueString.length) { return null; }
 
		if (months == 2) 
		{
			var isLeapYear:Boolean = isLeapYear(Number(years));
			if(isLeapYear && dates > 28) return null;
 
			if(!isLeapYear && dates > 29) return null;
		}
 
		if (((months == 4)||(months == 6)||(months == 9)||(months == 11)) && dates > 30) 
			return null;
 
		if(hours < 12 && ampm == "PM") {hours = (Number(hours)) + 12;}
		else if(hours > 11 && ampm == "AM") {hours = (Number(hours)) - 12};
 
		return new Date(years,(Number(months)) - 1,dates,hours,minutes,seconds,milliseconds);
	}
 
	public function getIntegerValue(dateString:String,index:int,min:int,max:int):Object
	{
		var valueString:String;
		for(var i:int = max;i >= min;i--)
		{
			valueString = dateString.substring(index,index + i);
			if(valueString.length < min) return NaN;
			if(isInteger(valueString)) return valueString;
		}
		return NaN;
	}
 
	private function isInteger(valueString:String):Boolean
	{
		var n:int = valueString.length;
		for(var i:int = 0;i < n;i++)
		{
			if(!isDigit(valueString.charAt(i)))
				return false;
		}
		return true;
	}
 
	private function isDigit(char:String):Boolean
	{
		return digits.indexOf(char) > -1;
	}
 
	private function getDayName(valueString:String,index:int):String
	{		
		var n:int = dayNames.length;
		var dayName:String;
		for(var i:int = 0;i < n;i++)
		{
			dayName = dayNames[i];
			if(valueString.substr(index,dayName.length) == dayName)
				return dayName;
		}	
		return "";
	}
 
	private function getDays(dayName:String):Number
	{		
		var day:Number = dayNames.indexOf(dayName) + 1;
		day = day > 12? day - 12:day;
		day = day == 0?NaN:day;
		return day;
	}
 
	private function getMonthName(valueString:String,index:int):String
	{
		var n:int = monthNames.length;
		var monthName:String;
		for(var i:int = 0;i < n;i++)
		{
			monthName = monthNames[i];
			if(valueString.substr(index,monthName.length) == monthName)
				return monthName;
		}	
		return "";
	}
 
	private function getMonths(monthName:String):Number
	{
		var month:Number = monthNames.indexOf(monthName) + 1;
		month = month > 12? month - 12:month;
		month = month == 0?NaN:month;
		return month;
	}
 
	private function isLeapYear(year:Number):Boolean
	{
		return ((year % 4 == 0) && (year % 100 != 0)) || ( year % 400 == 0);
	}
}

你是个软件架构师吗?

Wednesday, April 21st, 2010

The original copy comes from InfoQ


开发和架构的界限难以捉摸。有些人告诉你它根本不存在,架构只是开发者们所做的设计过程的简单扩展。 另外一些人认为这是一个鸿沟,它只能由那些做到高度抽象,而且不会陷入实现细节的开发者才能跨越。通常,在这两个极端的观点中间某处有个可操作的平衡点;不论如何,怎么从开发转换为架构师都是个有趣的问题。

经常被用来区分软件架构和软件设计开发的关键几点包括 伸缩性和抽象程度的增加以及作出正确设计决策意义的增强。软件架构是通过一个全局的观点,宏观的视角来理解软件系统作为一个整体如何工作。

即使这能够帮助区分软件开发和架构,它并不能帮助理解某人如何从开发提升到架构。 并且,它也不能帮助识别谁能够成为一个好的软件架构师,如果你想雇人的话你如何去寻找他们以及你是否是一个软件架构师。

经验可以判定但你需要更深入地了解

要成为一个软件架构师并不是一夜之间或者一个职位的提升就能简单达到的。 这是个职责,而不是头衔。这是个进化的过程,你将会逐步得到担当这个职责所需的经验和信心。

当你寻找架构师时,需要考虑各方面的素质,他们过去的经验往往是他们有能力担当这个职责很好的判断。由于软件架构师的职责是多种多样的,所以你需要再深入了解他们在不同领域的参与度,影响力,领导力和责任感。一般来说,在大多数项目中软件架构可分为两个阶段,架构的定义,然后是它的交付。

软件架构的定义

架构的定义过程看起来非常简单明了。 你需要做的是理解需求并设计一个系统来满足需求。 但实际上并没有那么简单,根据你不同的做法,软件架构的职责之间差距很大,以及如何认真看待自己的职责而定。如下图所示,这个职责的架构定义部分,可以进一步细分成不同的元素。

The role of a hands-on software architect from a definition perspective

  1. 管理非功能性需求:软件项目经常陷入问用户要求是什么,什么是他们想要的功能,但很少问他们需要什么非功能性需求(或系统质量)有时候,干系人会告诉我们,“这个系统必须很快”,但是这太主观了。非功能性需求如果要满足的话需要明确,可度量,可获得以及可测试。大多数非功能性需求本质上是技术层面的而且经常对软件架构有很大的影响。理解非功能性要求是架构师职责非常重要的一个部分,但假设这些需求是什么并不一定是对他们的挑战。你见过多少系统真正需要24×7的运行呢?
    Management of non-functional requirements
  2. 架构定义:捕捉到了非功能性需求后,下一步是开始思考你打算如何去解决干系人提出的这些问题并定义它的架构。 公平的说每个软件系统都有一个架构,但并不是每个软件系统都有一个定义好的架构。这正是问题的关键。架构定义过程让你想清楚你打算怎么在兼顾需求和限制的情况下把问题解决好。架构定义是将结构,方针,原则和领导力引入软件项目的技术层面。定义架构是作为软件架构师的工作,但是从头开始设计一个软件系统和对已存在的系统扩展是相当不同的。
    Architecture definition
  3. 技术选型:技术选型通常是一个有趣的练习,但它也有公平的挑战,因为你需要综合考虑成本、许可、供应商关系、技术策略、兼容性、协作性、支持、部署、升级的政策以及最终用户环境等各方面。综合这些因素,通常会导致简单选择类似富客户端技术而进入了完全的噩梦。接下来的问题就是这些技术是否能真正有用。技术选型是彻头彻尾的风险管理;复杂性或不确定性太高的时候要减轻风险,当有机会或利益的时候要引入风险。技术决策需要考虑多种因素,而且所有的技术决策需要被检查和评估。这包含软件项目的主要组成部分乃至开发中引入的类库和框架。如果定义一个架构,你还需要有信心认为选择这项技术是正确的。同样在技术评估中也还是存在开发新系统和向现有的系统增加新技术的不同点。
    Technology selection
  4. 架构评估:如果你设计软件,你需要问问自己你的架构是否有用。 对我来说,一个架构是成功的,如果它满足非功能性需求,而且为其他部分的代码提供必要的基础,并为解决和存在的业务问题提供足够的平台。软件的一个最大的问题就是它复杂而抽象,导致很难从UML图或代码本身去设想出运行时的特性。在软件开发周期中我们进行了很多不同类型的测试,这样我们能够有信心我们发布的系统在推出时能够正常运行。我们为什么不对架构也这样做呢? 如果能够测试你的架构,那你就可以证明它是有效的。如果你能尽早做到这一点,你就能减少项目失败的风险,而不是简单地希望一切都好。
    Architecture evaluation
  5. 架构协作:任何一个软件都不是与世隔绝的,需要很多人理解它。 包括从需要理解和切入架构的直接开发团队到其他对安全性、数据库、运营、维护、支持等有兴趣的干系人。要想让一个软件项目成功,你需要和所有的系统干系人紧密协作来保证架构和所在的环境很好的集成。不幸的是,现状是与开发团队的架构协作很少发生,更不要说外部干系人了。
    Architecture collaboration

软件架构的发布

对于架构的发布也是同样,对于成功的软件项目参与程度的不同,也决定了软件架构职责的不同。

The role of a hands-on software architect from a delivery perspective

  1. 拥有全局的视角:为了把一个架构成功地实现,我们需要具有全局的视角并把贯穿软件开发生命周期的愿景加以宣传与推广,必要的话在整个项目中展开和完善,并对成功发布负责。如果如果你定义了一个架构,参与并保持不断发展的架构才是有意义的,而不是选择把它传递给一个“执行小组”。
    Ownership of the bigger picture
  2. 领导力:拥有全局的视角是技术领导的一个方面,但是还有其他事情在软件项目发布阶段需要做。 这包括承担责任、提供技术指导、作出技术决策以及具有权力作出这些决定。作为架构师,你需要进行技术领导来确保每件事都被考虑到,而且团队在朝着正确的方向持续前进。软件架构师职位是需要内在领导力的,虽然这听起来很明显,但很多项目团队并没有获得他们所需要的技术领导,因为架构师认为一个成功的发布并不一定是他们所关注的问题。
    Leadership
  3. 教练和指导:在大多数软件开发项目中,教练和指导经常不被重视,团队成员得不到他们需要的支持。 虽然技术领导是引导整个项目,但个人也经常需要帮助。除此以外,教练和指导提供了一个强化技能的方式,并帮助提升职业生涯。这应该是软件架构师份内的事,而且指导团队架构和设计与帮他们解决代码问题是截然不同的。
    Architecture delivery
  4. 质量保证:即使是世界上最好的架构和领导,很糟糕的交付也足以让一个具备其他成功条件的项目失败。质量保证在架构师职责中占很大一部分,但这并不只是简单做代码检查。 比如,你需要一个基线来确保,这意味着引入新的标准和工作实践。从一个软件开发的角度来说,这可能包括代码标准、设计原则和源码分析工具甚至于使用持续集成,自动化单元测试以及代码覆盖工具。可以说大多数项目质量保证做的并不够,所以你需要搞清楚什么是重要的并给予它足够的保证。对于我来说,一个项目的重要部分包括架构上的重点,关键、复杂或高度可见的业务。你要关注实效并认识到你并不能保证一切,要知道做总比不做好。
    Quality assurance
  5. 设计、开发和测试:软件架构师的职责范围的最后一件事是设计、开发和测试。作为一个实际动手的架构师并不是需要你每天都要写代码,但是它的确意味着你一直在参与项目,而且积极帮助打造和交付它。说了这么多,为什么每天写代码不应该成为一个架构师职责的一部分呢?大多数架构师都有写代码的经验,因此让这些技能保鲜是有意义的。而且,架构师能体会到团队里其他人的痛苦和感受,这样能让他们更好地理解他们的架构从开发角度看是什么样的。很多公司有政策阻止软件架构师从事写代码,因为架构师“去做那些廉价的工作太贵了” ,这显然是个错误的态度…如果架构师已经花了那么多时间精力为项目做架构,何必从政策上不允许他们多走一步来帮助项目达到最终的成功呢?当然,有些情况下卷入代码级别并不现实。比如,一个大的项目通常意味有一个更大的“全局观” 来考虑它,而且可能有时候你就是没有时间。但一般来说,一个写代码的架构师比只在旁边观望要更高效和快乐。
    Design, development and testing

你是一个软件架构师吗?

不管你认为软件开发和架构之间的界限只是一个幻觉还是个巨大的鸿沟,以上强调了人们对整个软件架构中的经验水平往往有很大的差别,而这取决于他们怎么样工作以及他们如何认真地看待他们的职责。大多数开发人员不是在某一个星期一的早晨醒来就宣布自己成为一个软件架构师的。我当然也不是,我成为软件架构师的路线是一个渐进的过程。话虽如此,但很可能同样那些开发者已经做了一部分架构的工作,不论他们的职位名称是什么。

为软件系统的架构作出贡献和自己负责定义它有很大的区别,拥有持续的、跨不同领域的技能、知识和经验构成了软件架构的职责。跨越软件开发者和架构师的界限取决于你自己,但是首先你要明白你的经验水平,才能开始架构师之旅的第一站。

关于作者

你可以认为Simon Brown是一个写代码的软件架构师或者理解架构的软件开发者。当他没有用.NET或Java开发软件的时候,Simon通常在做咨询,指导或者培训。 Simon还写过关于Java的书,在行业活动做过演讲,并且整合了一个叫Software Architecture for Developers的培训课程, 该课程基于他在Coding the Architecture描 述的软件架构。你可以通过e-mail 或 Twitter 找到他。

查看英文原文Are You a Software Architect?

ICONS AND LOGOS ARE NOT THE SAME

Friday, April 16th, 2010

The original copy comes from Pixel Resort


I often stumple upon the confusion between icon and logo design. While logos may use the same visual vocabulary as icons, let there be no doubt; Icons and Logos are two completely separate design disciplines requiring different tools and different mindsets.

The gap between the designers vocabulary and the clients knowhow can cause some problematic confusions. To alleviate this lets look at what an icon is, what a logo is and how these two things could come to be confused.

WHAT’S AN ICON?
Apart from any religious denotations an icon is a graphical representation of a concept or operation. We use icons to bridge the understanding of abstract analogies and practical use. Icons can be used to illustrate an entire application or individual operations within that application. In short, icons help us understand and recognize concepts that might otherwise be pretty hard to grasp.

I could write a very long article about the whimsical nature of icon conventions and the semiotics that guide these, but in this case it’s more relevant to look at the technical differences that is so fundamental for icon design and how these differ from logo design.

ICONS ARE NOT SCALABLE
More than often, icons are not scalable. The very idea of icons are to best convey a given message within a predetermined confined visual space. In today’s iconcentric interfaces we allow for multiple variations of the same icon. The icons that are sitting in your dock most likely have atleast 5 different states embedded, making them appear crisp in all aspects of your interaction with them. List view in OSX gives you the 16×16 pixel version while the dock uses the 256×256 pixel adaptation. These are not scalable vector versions, they are handcrafted raster masterpieces. The creator must carefully select how to best take advantage of the canvas in any given size and more than often completely recreate the icon in those sizes.

My manilla mail icon in it’s various states. Note the different layout of the elements in the smaller sizes.

ICONS ARE QUADRATIC
Icons operate within a complete square canvas. How you choose to employ that canvas is up to you, but it’s restricted to that straight edged space.

Icons are created on a neatly defined and restricted canvas

So that’s it. Icons are not scalable, they’re handcrafted raster imagery born from the desire to objectify an operation or a concept within a confined visual space. How does this differ from a logo?

WHAT’S A LOGO?
A logo is a graphical element like an ideogram and/or a carefully arranged typeface that together forms a trademark or a brand. There’s an infinite amount of ways to think about logos and logo design. Again, the important thing here is to look at the technical differences from icon design.

LOGOS ARE SCALABLE
A logo should be completely scalable. A logo is the spearhead of a company’s commercial brand or any economic or non profit entity for that matter. Therefore a logo should be replicatable across many forms of media. This has great impact on the sort of mindset you need to bring when designing logos. We’re talking strictly vectorbased output and more than often, graceful degeneration of colours all the way down to uni colours.

Logos are supposed to be scalable.

LOGOS HAVE NO BOUNDARIES
Well in theory a logo could be anything. Other than the obvious benefits of working in a format that is easily scalable and replicatable there really is very little rules compared to icon design. Icon design is very influenced by technical dimensions and the restrictions of the systems that display them. Logo design is a completely different venue. A logo could be any shape, colour or dimension – it can be waved from a 100 feet banner or tattooed on a butt cheek. It’s only constraint is that of the physical media that will display it.

WHY ARE WE CONFUSED?
Icons have taken a very prominent role in modern interfaces. This has obviously spilled over to the realm of branding where many icons serve both as application icon and branding for that entity.

Panic creates excellent software and uses their application icons as product branding

This wave of iconism™ (yes, I just invented that for this purpose) has influenced many graphic designers and a lot of the appealing aspects of the cartoony and crafty iconized style has made it’s way to modern logo design trends. Infact this style has become the posterchild for the web 2.0 movement, and such many internetbased firms have logos that uses the same visual vocabulary as icons.

Logos inspired by an Iconistic style

And while logos can certainly employ an icon-like style, and even mimic the quadratic nature of icons. Let there be no doubt, Icons and Logos are two completely separate design disciplines. It’s important to know the difference between these two things, as they inheretly seek out to fulfil two very different goals, both technically and mentally.

Below I’ve included a PSD template that supplies you with the canvas in the correct dimensions for making your own icons. If you wanna talk icon or logo design throw me an email or just have a look at my services page.

If you liked this article, why not comment and/or tweet about it. You can also hit me up on that thing called twitter

论敏捷之生活方式和权威之实效使用

Thursday, April 15th, 2010

原文来自InfoQ


几个月前,我在InfoQ上发表了一篇文章。尽管我在其中只是少数几次用到“权威”一词,可有些读者还是对“权威”持有保留态度。这些有趣的回复激发我 再些一篇文章,以更好地说明我的观点。我会先从类比开始,稍后,我会指出如何在现实生活中实践敏捷;快到结尾时,我会再次从公众生活中给出一些例子,并将 其映射到敏捷和自组织的概念之上。

范例1

一个心脏病患者的目标是重获健康,医生给他提出如下建议:

1. 每天步行一个小时,不要吃油腻的事物,这样心脏就能强壮起来。不用吃药。(这种做法等同于完美的自组织。

很多患者遵循上面 的做法并且恢复了健康。

2. 病人没有遵守上述建议,病情恶化了。医生说病人需要吃药,甚至需要注射;但是仍然建议病人步行,不要进食油腻的事物。(等同于敏捷教练/推进者,希 望把病人的心脏调整好,因为病人没有遵守第一条建议,没有首先做好自组织。

很多病人在这个步骤之后恢复健康。

3. 现在,病人确实吃药了,但是方式有问题,而且还是没有听取第一条建议(步行和良好的食物)。病情进一步恶化。现在需要拯救病人了,世界上所有的医生都会建 议采取心脏旁路手术,或其他类似手术,否则病人就会死掉。(这等同于权威

现在,你还要花多长时间继续教育病人关于步行和食 物的好处?他根本不听, 而且快要见上帝了。你会把他从家庭和社会里抛弃掉么?在我看来,如果你希望病人好转,你会请求医生采取任何措施,请他拯救这个病人。这就是我对于权威的态度。

范例2

我有个朋友开了一家软件公司,使用敏捷作为开发方法论。他告诉我:

1. 他的团队非常松懈。他们甚至不去遵循敏捷最基本的原则:与客户的产品负责人(以下简称PO)协作。他们在演示后甚至不会去给PO打电话了解反馈。PO在回 顾中会说当前的sprint失败了。团队根本没有理解需求,很多细节没有深入讨论。我的朋友解释了与PO协作的好处,并鼓励团队在演示后采纳PO的反馈。

2. 团队没有听从建议。他们再一次没有与PO协作,而且也不觉得有必要在演示后了解PO的反馈。再一次,Sprint失败了,PO上报了问题。我的朋友再次尝 试鼓励团队。

3. 团队以自由、傲慢和信任的名义,仍没有听从。Sprint失败了,连续三次。PO准备放弃这个项目。

4. 现在我的朋友说:“你们最好跟随我的指示。如果下一个sprint再次失败,我就会把你们开除掉!”现在,团队不得不走出他们的舒适区域。直到今天,项目 进展非常顺利!现在团队也在更广的层面上成为了自组织团队。但是必须有人至少展示一次权威,让事情走上正规。这正是我对于“实效权威(pragmatic authority)”的看法。

范例3

我另一个熟识的人打算把自己所有的钱都投入到一个小公司的股票上。他非常自信(而不是傲慢),认为自己做出了非常明智的投资,将会为自己带来高额回报。他 的父亲却心存怀疑,试图建议他不要向这个公司投资。我的朋友以自由的名义没有听从父亲的建议,而且对于自己的做法过于自信。然后他的父亲用权威阻止了他。 朋友很沮丧地遵循了父亲的建议,因为他很尊重自己的父亲。后来,我们都看到这家公司的倒闭,人们也都作鸟兽散。朋友感谢他父亲的权威,这让他避免了破产的窘境。

生活中,有些决策一旦做错,我们总是可以恢复过来,得到第二次机会,进行修正。然而有些决策却并非如此,生活不会给你第二次机会。如果出现错误,将会不可 扭转地改变你的生活、你的发展、你的项目、你与客户的关系、等等等等。在这些事情上,我们应该听从专家和更有经验的人的判断。如果他们所爱的人正要在毫无 知觉的情况下犯错,这些专家在某些场合也许会动用权威。这就是我对于“实效权威”的看法。

范例4

一个小孩拿着一把刀,坚持要他父亲和他一起玩。毫无疑问,他会伤到自己和他父亲,因为他还没有成熟到可以玩刀的年纪。他的父亲首先尝试跟他解释说不要玩 刀,但孩子就是孩子,根本不听。父亲接下来使用自己身为父亲的权威,把刀抢了过来。尽管孩子为此哭泣了一段时间,但是孩子和父亲因此避免受伤。此时,孩子 的哭泣和精神痛苦,相对于可能的肉体伤害来说就没有那么重要了。

这个孩子的例子与敏捷的话题背景放在一起有点滑稽,不过我相信:在我们人生的每个阶段,我们的内心都有一个孩子。这些阶段包括:

  • 缺 少知识(孩子对刀没有完全的认知)
  • 顽固(孩子太喜欢刀了,不会轻易放弃)
  • 不够成熟(当然,孩子的年纪还不知道玩刀的 风险)
  • 贪婪(给的玩具越多,孩子要的越多)

可笑的是,在我们人生的不同阶段(比如中学毕业、大学毕业、开始工作、得到升职后等等),我们都觉得自己不再是孩子了。在我看来,上面这些特点都是 我们内在的孩子所展示出来的,而且我们总是要常常表现出来,不过明显是以一种经过修饰的方式,并且处于不同的场合。常常是缺少知识的状况。有人敢宣称自己 知道一切么?如果需要改变工作方式、行为方式和沟通方式的时候,我们就会变得不那么灵活。我们不是总想在自己的舒适区域内顽固地工作下去么?谁敢宣称自己 已经到达了成熟的最高阶段?我们中有多少人希望听从指令?我们对于已有的金钱、自由、技术或是其他东西满意吗?我们都是长大了的孩子。

那 么,如果孩子需要权威,那么大人也一样,不过当然是以经过修饰的方式。

敏捷是一种生活方式

我常常把人们在办公室和公共生活中的表现做关联。我们在办公室里面的行为是无法独立看待的。实际上,我们在工作场合中所采取的工作方式、行为表现和软技 能,都是我们生活中性格的映射。在我看来,无法把一个人在个人生活和职场中的表现分开看待。我只在一些好莱坞电影中见过分裂的人格。

我想把(敏捷所赋予的)授权和自由这两个概念扩展到日常的社会生活中。我认为:法律和政策不应由国会或参议院制订,而是应该授权草根阶层的人们管理 国家。不仅如此,老百姓们应该决定政治家和官僚们的薪水,同时负责决定他们的表现是否合格拿到薪水。应该有专人(等同于敏捷教练)持续不断地指导人民和社 区养成良好习惯、坚强的人格,以及所有我们在学校学到的神圣的道义。我真诚地相信这是现实而可能的,因为人类有意愿这么做。但即使如此,我们也不能排除警 察的存在。在这个宇宙中,人类是最复杂、最不可预测的生物。就像技术无法解决所有的难题一样,医学无法生产出所有的药品来治疗所有的疾病(有些疾病是无法 治愈的)。同样,你也无法激励和启发所有人,无法做到让所有人都能真正地自组织。如果这是真的,对于不走正道、做不到敏捷、也就是无法自组织的人,你该怎 么办?这些人也是人啊,他们有生命,理应得到尊重和拯救。他们应该得到不同的对待,以避免其他生命受到他们的影响。长久以来,对待这样的人最有成效的方式 就是使用权威控制他们不成熟的一面,当然要以职业的方式使用。

敏捷所有的理念都是非常出色的,我对于以敏捷写成的任何东西都很热衷。如果我 们能把这些理念应用到实际公共生活中,我们就能改变世界。

现实生活与敏捷理念

  • 每个国家和文化都有鼓舞人心的领导者和传教士,他们宣扬和平、人性和圣心(这比敏捷教练和推进者的范围和影响力可要大多了),可是为什么 犯罪率还是不断上升呢?
  • 每个国家都有警察,警察有权威。如果有人认为激励能够解决所有的问题,那我能问一下为什么每个国家都有警察吗?
  • 为 了监控交通,我们用摄像头抓拍超速的车辆。即使几乎所有人都遵守交通故障,可这没有让摄像头显得多余。为什么我们不能相信人民,并消除管理这些摄像头的额 外开销呢?
  • 与上一点类似,红绿灯告诉我们何时该停、何时该行。为什么我们不能去掉所有的红绿灯呢?自组织的人民可以以自组织的方式行 驶,不是么?
  • 只要是有犯罪活动,不管是轻微犯罪,还是像炸弹袭击这样的重大犯罪,政府总是要派警察和军队去控制局势。如果我们能够按照 理想的方式,那么政府只要派牧师和圣徒去启发罪犯就可以了。我想这可能不太实用吧?

我把现实生活的行为与IT行业中的敏捷作比较,这听起来有点诡异。实际上,我是在拿人和人作对比。那些有罪恶企图的人和我们生活在同一个世界。我们需要红绿灯、摄像头、警察来维持公共秩序,同样是这样一群人,为什么走进办公室之后,我们就不再需要任何监督和管理了呢?敏捷是一种杰出的软件开发方法,不过在 我看来,它也不是拥有神奇力量的万灵丹,不会把走进办公室的人们瞬间变成完美的人。

公司中有种种罪恶,比如诽谤、嫉妒、大声喊叫以示炫耀、不与人分享知识,还有拍马屁等等。身染这些罪恶的人就跟欠公司钱的人一样,要以不同方式对待。长久 以来,使用权威就是对待他们的“不同方式”。我称之为“现实的理想主义”。我相信:“盲目的理想主义”无论在任何场合都不会成功。

我们在学校里的书本上都读到过:“永远讲真话,绝不讲谎言。”与此同时,书上也告诉我们:能够拯救生命的谎言并不坏。拯救生命要远为重要。与之类似,权威 可能不好,可有时为了拯救某个人、某项业务、某个项目或是某个sprint,就像上面的例子一样,权威又是不可避免的。

我认为:问题在于误用权威和在不必要的时候使用权威。它应该被用来保护事业,保护客户,保护项目,这都能间接地保护和帮助人。敏捷不仅仅是一系列规则,它 的范围更广。它需要我们的思考方式和工作态度在范式的层面做出迁移。仅仅在教室里接受几天的培训不可能完全将敏捷实施好。在变更、演化、自我激励和诚实意 图这些方面的能力和成熟度上,它都有一些基础的要求,这样我们才能作为一个团队表现杰出;同时,敏捷还需要经常的监督。

结论

我认为自由和权威是有联系的。我发现某些敏捷的实践者对于“权威”这个词汇有着过敏反应。在我看来,问题不在于权威,而在于人们常常误用权威。有些人认为 权威是享受、权力和社会地位的象征。自由与权威有同样的属性。如果人们误用自由,结果同样可怕。有人认为有了自由就可以享受、不担义务、不负责任,等于拥有了杀人执照(license to kill),可以作任何事情。这样的人应该永远不给予自由,连受控制的自由也不行。公司解雇某个人,是权威的终极用法,即使采纳敏捷的公司也会解雇人员。 我想:在日常的项目和业务进程中,如果我们在有必要使用权威时小心而谨慎,我们也许不必以最终极的方式使用权威——解雇。底线在于:权威和自由——误用则 坏,以崇高之目的和正确之精神,善用则灵。

关于作者

Vinay Aggarwal是印度Xebia IT Architects的交付经理。他在IT业界有11年的经验。他拥有工程学学士学位,是PMI认证的项目管理专家(PMP)和经认证的Scrum Master(CSM)。曾在IBM和埃森哲等公司任职。他在瀑布和敏捷(Scrum)方法论上都有很多经验。他信奉横向思维,并将管理学概念应用于解决 各种交付上的挑战。

查看英文原文Agile – A Way of Life and Pragmatic Use of Authority