ebook img

IEEE Software (November/December) PDF

68 Pages·2002·3.399 MB·English
by  
Save to my drive
Quick download
Download
Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.

Preview IEEE Software (November/December)

software construction Editors: Andy Hunt and Dave Thomas (cid:2) The Pragmatic Programmers andy@pra gmaticprogrammer.com (cid:2) [email protected] State Machines Dave Thomas and Andy Hunt W e are surrounded by real-world Stating the obvious state machines: ballpoint pen re- A state machine is a system with a set of tractor mechanisms, vending ma- unique states. One state is special—it repre- chines, washing-machine con- sents the system’s initial state. One or more of trollers, digital watches. They are the other states are final states; when an event a trivial but underused technol- causes us to reach one of these the state ma- ogy that can simplify how we implement chine exits. States are connected by transitions. programs that must track how they got to Each transition is labeled with the name of an their current state before handling a new input event. When that event occurs, we fol- low the corresponding transition from the cur- rent state to arrive at the new state. State ma- chines are often represented as diagrams with the states shown as circles and the transitions as labeled arrows between the states. Figure 1 shows a simple state machine that exits when a set of coin tosses results in a head, then a tail, and then another head. It starts in state S . If 0 we toss a tail, the transition loops back and we stay in S ; otherwise, we move on to S . 0 1 This state moves to S if we toss a tail next. 2 From there we move on the S , a final state, 3 event. However, many programmers feel if we see another head. This type of state ma- that state machines are only useful when chine is sometimes called a deterministic fi- they’re developing communication protocol nite state machine or automaton. The graph stacks, which is not an everyday activity. in Figure 1 is a state transition diagram. This is unfortunate. State machines can be appropriate in surprising circumstances. Using state machines Correctly applied, they will result in faster, A state machine is useful whenever we more modular, less coupled, and easier to have a program that handles input events maintain code. State machines make it easy and has multiple states depending on those to eliminate duplication, honoring the DRY events. These situations arise frequently in principle.1 They also let you write more ex- communications, parsing, emulations, and pressive code, because you can specify intent handling user input. You can spot a program and implementation independently. These that’s a candidate for a state machine by are all good, pragmatic, reasons to investi- looking for code that contains either deeply gate them further, so let’s look at some sim- nested ifstatements or many flag variables. ple state machine implementations and You can eliminate the flags and flatten the problems they can solve. nesting using a state machine. 10 IEEE SOFTWARE November/December 2002 0740-7459/02/$17.00 © 2002 IEEE SOFTWARE CONSTRUCTION A while ago, Dave wrote a simple Web-based order-handling system. Tail Customers could pay by check or purchase order. When orders were Start Head Tail Head S S S S initially entered, a confirmation was 0 1 2 3 mailed. When payment was received, the products were shipped. If that Tail Head payment was a purchase order, the program generated an invoice and tracked its subsequent payment sta- Figure 1. A state machines that exits for a head-tail-head sequence tus. Because events could occur of coin tosses. weeks apart, the status had to be tracked in a database. Figure 2. A state After a while, the code started to !isalpha(ch) transition Start get messy and handling the special diagram that cases began to get ugly. So, Dave counts words in eof reimplemented the code as a simple S eof our text input. state machine. This state machine ended up having a dozen or so states isalpha(ch) !isalpha(ch) and perhaps 15 action routines to [count++] deal with transitions between these states. The resulting code was a lot eof W clearer (and a lot shorter). And when the customer changed the applica- tion’s business rules, typically Dave isalpha(ch) just changed a few entries in the table that defined the state transitions. However, that’s a fairly complex example. Let’s look at something Start * * * simpler, such as a program that counts words in text. Here the input ch == ’<’ ch == ’ ” ’ events are the characters we read, S Cmd Str and the states are “W: in a word” ch == ’>’ ch == ’ ” ’ and “S: not in a word.” We can in- !isalpha(ch) * crement the word count whenever [count++] we transition from S to W. Figure 2 shows the state transition diagram. ch == ’<’ W Note that we’ve added a semantic ac- tion to one of the transitions—we in- crement a count on the S →→ W tran- isalpha(ch) sition. On its own, this example might not be particularly com- pelling—the code required to imple- Figure 3. A state machine that counts words in HTML. We omitted ment the state machine word counter the eof transitions (shown in Figure 2) for clarity. is probably about the same size as the conventional version. However, say our requirement changed slightly— we’d now need flags for “skipping a cally we’ll have a case statement to our client tells us that the program command” and “in a quoted string.” handle the different states or events. should now handle HTML files, ig- With the state machine, it is a simple However, once we start becoming noring any text between “<” and extension, shown in Figure 3. more complex, we convert the state “>”. We also deal with quoted transition diagram to a 2D table. The strings, so that “Now is the <IMG Implementing state machines table is indexed by the current state src="clock.gif" alt="<time>"> For very simple state machines, we and the input event, returning the re- for all good people” should count find it is easiest to implement the sulting next state. It’s convenient to seven words, and correctly ignore the states and transitions manually. We include an action code in each table “>” in the quoted string. If we had use a variable to keep the current state entry too, because this tells us what taken the conventional approach, and update it as events happen. Typi- to do on each transition. These table November/December 2002 IEEE SOFTWARE 11 SOFTWARE CONSTRUCTION entries are conveniently represented enumerations or possibly as a set of as structures or simple data-only function pointers. Robert Martin of classes. A more sophisticated imple- Object Mentor implemented state ma- mentation could replace these table chine compilers for Java and C++ based HHooww ttoo entries with objects that include the on these principles. You can download behavior to be performed. Applica- them from www.objectmentor.com/ RReeaacchh UUss tions coded this way often have a resources/downloads/index. trivial main loop: State machines and object-oriented development Writers while eventPending(){ For detailed information on submitting articles, event = getNextEvent(); If you’re working in an object- write for our Editorial Guidelines (software@ entry = transitions oriented environment, the same basic computer.org) or access http://computer.org/ [currentState][event]; principles apply. However, you can software/author.htm. entry.executeAction(); also use classes to provide a clean in- currentState = terface to the thing being modeled. In Letters to the Editor entry.nextState(); Design Patterns,2 the Gang of Four Send letters to } present the State pattern. Their ex- ample is a TCP connection. As the Editor, IEEE Software Once we have a program in this connection changes state (presum- 10662 Los Vaqueros Circle form, we can easily change it as new ably driven by an internal state tran- Los Alamitos, CA 90720 requirements come along. For exam- sition system similar to the ones we [email protected] ple, if our client suddenly wants us to discussed earlier), the connection ob- Please provide an email address or count HTML commands, we merely ject changes its behavior. When the daytime phone number with your letter. add a new action to the S→→Cmd and connection is in the closed state, for W →→ Cmd transitions in the table. If example, a call to open it might suc- On the Web we notice that we’re handling HTML ceed. However, if the connection is Access http://computer.org/software for comments incorrectly, we just add a open already, the same call will be re- information about IEEE Software. couple of new states and update the jected. This is a tidy approach to table accordingly—the main program managing the external interface to a Subscribe doesn’t change at all. state driven system. Visit http://computer.org/subscribe. More sophisticated implementations Subscription Change of Address Once you get into the realm of S Send change-of-address requests for magazine large state machines, maintaining the tate machines are an underused subscriptions to [email protected]. state table manually becomes an er- tool. The next time you find your- Be sure to specify IEEESoftware. ror-prone chore. Rather than coding self adding “just one more flag” the table directly in our implementa- to a complex program, take a step tion language, we normally write a back and see if perhaps a state ma- Membership Change of Address plain text file containing a simpler chine might handle the job better. Send change-of-address requests for IEEE representation and use this to gener- and Computer Society membership to ate code. For the HTML word [email protected]. counter, our state file might start something like: Missing or Damaged Copies If you are missing an issue or you received a damaged copy, contact [email protected]. S: LT →→ (CMD NONE), References WORD →→ (W INC), 1. A. Hunt and D. Thomas, “Don’t Repeat Your- self,” The Pragmatic Programmer, Addison- Reprints of Articles default →→ (S NONE) Wesley, Boston, 2000. For price information or to order reprints, W: LT →→ (CMD NONE), 2. E. Gamma et al., Design Patterns, Addison- W →→ (W NONE), Wesley, Boston, 1995. send email to [email protected] or fax default →→ (S NONE) +1 714 821 4010. Depending on the target language, Reprint Permission we might then generate from this a To obtain permission to reprint an article, header file containing the definitions of Dave Thomasand Andy Huntare partners in The contact William Hagen, IEEE Copyrights and the states, events and actions, and a Pragmatic Programmers, LLC. They feel that software consultants Trademarks Manager, at [email protected]. who can’t program shouldn’t be consulting, so they keep current source file containing the transition by developing complex software systems for their clients. Con- table. The actions could be defined as tact them via www.pragmaticprogrammer.com. 12 IEEE SOFTWARE November/December 2002 design Editor: Martin Fowler (cid:2) ThoughtWorks (cid:2) [email protected] Using Metadata Martin Fowler I occasionally come across people who tedious programming. This is when you describe their programming tasks as te- should consider using metadata. dious, which is often the sign of a de- To illustrate the approach, consider a sim- sign problem. One common source of ple design problem: build a module that will tedium is pulling data from an external read data out of a simple file format into source. You almost always do the same memory. One example of this file is a tab- thing with the data, but because the data delimited format with the first line containing differs each time, it’s difficult to reduce such the names of the fields (see Table 1). Explicit and implicit reads class ExplicitReader... public String FileName; Figure 1 offers perhaps the most TextReader reader; straightforward approach to this static char[] SEPARATOR = {‘\t’}; problem—reading each column of data into a record structure. As a public ExplicitReader (String fileName) { program, it’s pretty simple, because FileName = fileName; } it’s easy to read and to write. Trou- public IList ReadBatsmen() { ble rears, however, if you have a lot IList result = new ArrayList(); of files to read. You have to write reader = File.OpenText (FileName); this program for each file, which is reader.ReadLine(); //skip header a tedious job, and tedium usually String line; while ((line = reader.ReadLine()) != null) { has a bad smell—indicating worse String[] items = line.Split(SEPARATOR); troubles. In this case, the trouble Batsman bat = new Batsman(); would be duplication—always bat.Name = items[0]; something worth avoiding. bat.Matches = Int32.Parse(items[1]); bat.Innings = Int32.Parse(items[2]); bat.Runs = Int32.Parse(items[3]); Table 1 result.Add(bat); } Actual listing 1 return result; Name Matches Innings Runs } DG Bradman 52 80 6,996 } RG Pollock 23 41 2,256 public class Batsman... GA Headley 22 40 2,190 public String Name; H Sutcliffe 54 84 4,555 public int Matches; AC Gilchrist 31 44 2,160 public int Innings; public int Runs; E Paynter 20 31 1,540 KF Barrington 82 131 6,806 ED Weekes 48 81 4,455 Figure 1. A simple, explicit solution for reading data from a tab-delimited WR Hammond 85 140 7,249 file. 0740-7459/02/$17.00 © 2002 IEEE November/December 2002 IEEE SOFTWARE 13 public class ImplicitReader... Figure 2. An implicit public String FileName; design solution for TextReader reader; reading in data from static char[] SEPARATOR = {‘\t’}; multiple files. public ImplicitReader (String fileName) { FileName = fileName; } public IList Read() { IList result = new ArrayList(); reader = File.OpenText (FileName); IList headers = parseHeaders(); String line; while ((line = reader.ReadLine()) != null) { result.Add(parseLine(headers, line)); } return result; } IList parseHeaders() { IList result = new ArrayList(); String[] items = reader.ReadLine().Split(SEPARATOR); foreach (String s in items) result.Add(s); return result; } IDictionary parseLine (IList headers, String line) { String[] items = line.Split(SEPARATOR); IDictionary result = new Hashtable(); for (int i = 0; i < headers.Count; i++) result[headers[i]] = items[i]; return result; } abstract class AbstractReader { public AbstractReader (String fileName); FileName = fileName; } public String FileName; protected TextReader reader; protected static char[] SEPARATOR = {‘\t’}; public IList Read() { IList result = new ArrayList(); reader = File.OpenText (FileName); skipHeader(); String line; while ((line = reader.ReadLine()) != null) { String[] items = line.Split(SEPARATOR); result.Add(doRead(items)); } return result; } private void skipHeader() { reader.ReadLine(); } protected abstract Object doRead (String[] items); } class ExplicitReader2 : AbstractReader ... public ExplicitReader2 (String fileName) : base (fileName){} override protected Object doRead(String[] items) { Batsman result = new Batsman(); result.Name = items[0]; result.Matches = Int32.Parse(items[1]); Figure 3. An explicit de- result.Innings = Int32.Parse(items[2]); sign that uses substitu- result.Runs = Int32.Parse(items[3]); return result; tion on the variable part } of the program. Figure 2 offers one approach to advantage is that this single program a hundred of these kinds of files to avoiding this tedium, a generic way will read in any file, providing it fol- read, writing a single program like to read in any data from a file. The lows the general format. If you have this takes a lot less effort than writ- 14 IEEE SOFTWARE November/December 2002 public class ReflectiveReader ... public String FileName; TextReader reader; static char[] SEPARATOR = {‘\t’}; public Type ResultType; public ReflectiveReader (String fileName, Type resultType) { FileName = fileName; ResultType = resultType; } public IList Read() { IList result = new ArrayList(); reader = File.OpenText (FileName); IList headers = parseHeaders(); String line; while ((line = reader.ReadLine()) != null) { result.Add(parseLine(headers, line)); } return result; } IList parseHeaders() { IList result = new ArrayList(); String[] items = reader.ReadLine().Split(SEPARATOR); foreach (String s in items) result.Add(s); return result; } Object parseLine (IList headers, String line) { String[] items = line.Split(SEPARATOR); Object result = createResultObject(); for (int i = 0; i < headers.Count; i++) { FieldInfo field = ResultType.GetField((String)headers[i]); if (field == null) throw new Exception (“Unable to find field: “ + headers[i]); field.SetValue(result, Convert.ChangeType(items[i],field.FieldType)); } return result; } Object createResultObject() { Type[] constructorParams = {}; return ResultType.GetConstructor(constructorParams).Invoke(null); } } Figure 4. A reflective programming design. ing an explicit program (as in Figure could just pass the block of assignment class’s metadata, we can also determine 1) for each file. statements in as a function argument. the types for the target class’s fields. The problem with this generic style By parameterizing the assignment This lets us handle the type conversions is that it produces a dictionary, which is statements, you can reduce duplica- properly. easy to access (especially when your tion. You can also reduce—but not language supports a simple index mech- eliminate—the tedium. All those as- Two ways of using the anism as C# does) but is not explicit. signments still must be written, both metadata Consequently, you can’t just look at a for reading and writing (if you are sup- We can use the metadata in two file declaration to discover the possible porting both). However, by taking ad- ways: reflective programming andcode fields you must deal with, as you can vantage of the metadata in both the generation. The reflective program- with the Batsmen class in Figure 1. Fur- target class and file structure, you can ming approach leads us to a program thermore, you lose all type information. avoid writing any assignments at all. that uses reflection at runtime to set So, how can you have your explicit The metadata is available in two field values in the target class (see Fig- cake while eating only a small amount forms. The field heading at the top of ure 4). Many modern platforms pro- of code? One approach is to parame- the data file is a simple metadata that vide this kind of runtime reflection. terize the assignment statements from supplies the field names (XML tag The resulting reader class can read any Figure 1 by enclosing them in a single names give the same information). If file that conforms to the format and substitutable function. Figure 3 does the target class’s fields match the data has a matching target class. this with the object-oriented style of an file’s names (or if we can make them The code generation style aims to abstract superclass. In a more sophisti- match), we have enough to infer the as- generate a class that’s similar to the cated programming language, you signments. If we can query the target hand-written one in Figure 3. We can November/December 2002 IEEE SOFTWARE 15 public class ReaderGenerator ... String DataFileName; Type Target; String ClassName; TextWriter output; public void Run() { Console.WriteLine(output); output = new StringWriter(); writeClassHeader(); writeConstructor(); writeDoRun(); writeClassFooter(); Console.WriteLine(output); writeOutputFile(); } void writeClassHeader() { output.WriteLine(“using System;”); output.WriteLine(“namespace metadata”); output.WriteLine(“{“); output.WriteLine(String.Format(“class {0} : AbstractReader “, ClassName)); output.WriteLine(“{“); } void writeClassFooter() { output.WriteLine(“}”); output.WriteLine(“}”); } void writeConstructor() { output.Write(String.Format (“\t public {0} () : base (\”{1}\”)”, ClassName, DataFileName)); output.WriteLine(“{}”); } static char[] SEPARATOR = {‘\t’}; void writeDoRun() { output.WriteLine(“\toverride protected Object doRead(String[] items) {“); output.WriteLine(String.Format (“\t\t{0} result = new {0}();”, Target)); writeFieldAssignments(); output.WriteLine(“\t\treturn result;”); output.WriteLine(“\t}”); } void writeFieldAssignments() { TextReader dataReader = File.OpenText (DataFileName); String[] headers = dataReader.ReadLine().Split(SEPARATOR); dataReader.Close(); for (int i = 0; i < headers.Length; i++) { FieldInfo field = Target.GetField((String)headers[i]); if (field == null) throw new Exception (“Unknown Field: “ + headers[i]); output.WriteLine(String.Format( “\t\t result.{0} = ({1})Convert.ChangeType(“, headers[i], field.FieldType)); output.WriteLine(String.Format( “\t\t\titems[{0}],typeof({1}) );”, i, field.FieldType)); } } void writeOutputFile() { StreamWriter outFile = new StreamWriter(File.Create(ClassName + “.cs”)); outFile.Write(output); outFile.Close(); } } Figure 5. A generator. use the style presented in Figure 1, be- ure 6 shows the resulting class. Al- languages often make good languages cause we don’t have to worry about though I’m using the same language in for generation due to their powerful duplication in the generated code. Fig- this case, there’s no reason why the string handling. ure 5 shows the kind of class we could generator must be the same language The generator also uses the lan- use to perform the generation, and Fig- as the class it’s generating—scripting guage’s reflection capabilities to deter- 16 IEEE SOFTWARE November/December 2002 Figure 6. Example using System; namespace metadata code that Figure 5 { generated. class ExplicitReader3 : AbstractReader { public ExplicitReader3 () : base (“batsmen.txt”){} mine the field types; how- override protected Object doRead(String[] items) { metadata.Batsman result = new metadata.Batsman(); ever, it does it at compile result.Name = (System.String)Convert.ChangeType( time rather than at runtime. items[0],typeof(System.String) ); The generated classes don’t result.Matches = (System.Int32)Convert.ChangeType( use the language’s reflection items[1],typeof(System.Int32) ); capabilities. result.Innings = (System.Int32)Convert.ChangeType( items[2],typeof(System.Int32) ); Given these two styles of result.Runs = (System.Int32)Convert.ChangeType( metadata-based programs, items[3],typeof(System.Int32) ); the obvious question is return result; when to use each style. The } reflective program offers a } } single compact class to carry out the mapping. There are, however, some disadvan- tion is done with every significant wouldn’t bother for a few classes. I’d tages. Many people find reflection change—the best way of doing this is just use a technique to separate the somewhat hard to use, and it might to make it part of an automated build varying code from the constant code. defeat some of your environment’s process. With many files, generation I can’t give a hard number for when tooling, such as intelligent reference might lead to a larger code bulk, which it’s better to use metadata—it’s more searches and automated refactorings. might affect footprint and build times. a reflection of the degree to which the In addition, in some environments, re- I usually prefer generation to reflective assignment’s monotony is affecting flective calls can be significantly programs, but you have to weigh your development. slower than direct method calls. decision based on your concerns. Generation also has its problems. T You need discipline to ensure that de- here’s also the question of whether Martin Fowleris the chief scientist for ThoughtWorks, an velopers don’t hand-edit the generated to use metadata-based techniques Internet systems delivery and consulting company. Contact him files. You must also ensure that genera- at all. For something like this, I at [email protected]. focus guest editors’ introduction Software Engineering as a Business Ann Miller, University of Missouri-Rolla Christof Ebert,Alcatel o matter what business you are in, you are also part of the software N business. Software makes the world go round—at ever increasing speeds. Computer-based, software-driven systems pervade today’s so- ciety. From avionics flight control to the ubiquitous computers in per- sonal digital assistants and cellular phones, to automotive and consumer elec- tronics, software provides features and functions in daily use. Increasingly, system functional- ity is implemented in software. Where we used to split hardware from software, the business case entirely determines such bound- aries now—what we best package at which level in which compo- nent, be it software or silicon. For example, a TV set in the 1970s had no software, whereas today its competitive advantages and the majority of engineering efforts are software-driven. The software business, how- ever, has manifold challenges, ranging from the creation process and its inherent risks to direct bal- ance sheet impacts. For example, the Standish Group found in its survey (2000 edition of the Chaos Report) that only 26 percent of the projects finished on time and within budget and a staggering 28 percent were canceled before de- 18 IEEE SOFTWARE November/December 2002 0740-7459/02/$17.00 © 2002 IEEE livery. Moreover, the remaining projects, level in the company and track them con- which all finished late, over budget or both, tinuously against actual performance. We Introducing delivered only a fraction of the planned must manage changes in the business cli- functionality (www.standishgroup.com). mate, or they will ripple through uncon- a product to Introducing a product to market late trolled. And the committed targets must be market late loses market share; canceling a product followed through! We therefore present the before it ever reaches the market sinks Balanced Scorecard approach. Each com- loses market scarce R&D funds. Not only is software pany and software team can introduce this share; increasing in size, complexity, and per- approach to focus on the right things and canceling centage of functionality, it is increasing in balance short-term (survival) needs with contribution to the balance sheet and medium- to long-term investments and im- a product profit-and-loss statements. To make mat- provements. before it ters worse, requirements are easily and As the saying goes, time is money; there- frequently changed. A recent study by the fore, wasted development time is lost rev- ever reaches U S National Institute of Standards and enue. Thus, processes and methods that im- the market Technology reports that insufficient soft- prove our ability to deliver reliable, quality sinks scarce ware testing costs the US as much as software are important. US$59 billion a year and that up to US$22 Security is a quickly growing software R&D funds. billion of that could be saved if licensed business. Recent examples for business software had just 50 percent fewer defects models include the sale of hacker insur- (NIST, The Economic Impacts of Inade- ance, which works to keep corporate Web quate Infrastructure for Software Testing, sites from defacement or denial-of-service Washington, D.C., 2002; see also the attacks by hackers and protects databases, News report on p. 97). such as those maintaining credit card in- This special issue is devoted to the busi- formation. The more we share and net- ness of software engineering. We explore work, the more we are exposed to attacks some of the critical factors associated with of all kinds. The exploding need for secure succeeding in today’s high-tech software software and protection schemes for our businesses and discuss skills, knowledge, business processes,end-to-end, indicate this and abilities that software practitioners impact. Our Point/Counterpoint discussion need to improve their business decision- takes up one example from the security do- making capabilities. We illustrate business main and illustrates two ways to approach cases to supplement technical arguments for software security and how that decision rip- process and technology improvement. We ples into business decisions. also address how software engineering can help small and large businesses as well as start-ups. We have not explicitly addressed Every business needs good communica- the “technical career track” ver- tion to be successful and reduce friction, sus the “management ladder.” whether it is from engineer to manager, We believe that such discussions are individ- manager to engineer, or engineer to engi- ual choices. However, we do hope that all neer. It is easy for a company to relegate software practitioners value their stake in software to a low priority when it is focus- their respective software business decisions ing on other technologies in its products. and that these pages offer ideas for increas- Software engineers must speak out clearly ing your return on investment in that busi- and be heard and understood by manage- ness. The “Suggested Reading” sidebar offers ment. Both sides must learn how to address more food for thought. each other’s real needs. Management doesn’t Whether your customer is internal to care for technical jargon, and engineers are your company or a traditional external easily confused with capitalization and de- client or user of your product, whether the preciation questions about their software. product is shrink-wrapped and shipped or The brief article on translating “software a service or embedded system, customer developer speak” to “management speak” satisfaction is part of good business and of and vice versa can help here. good software. Here’s to you and your We must consistently set targets on each customer. November/December 2002 IEEE SOFTWARE 19

See more

The list of books you might like

Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.