Chapter 3. Varying the Algorithm with the Template Method

Imagine that you have a complex bit of code—maybe an involved algorithm or just some hairy system code or perhaps something just hard enough that you want to code it once, write some unit tests, and leave it alone. The trouble is that somewhere right in the middle of your complex code is a bit that needs to vary. Sometimes this bit of code wants to do this and sometimes it wants to do that. Even worse, you are pretty sure that in the future the silly thing will need to something else. You are up against the old “defend against change” problem discussed in Chapter 1. What do you do?

To make this scenario more concrete, imagine that your first real Ruby project is to write a report generator—that is, a program that will spew out monthly status reports. Your reports need to be formatted in reasonably attractive HTML, so you come up with something like this:


   class Report
     def initialize
       @title = 'Monthly Report'
       @text =  [ 'Things are going', 'really, really well.' ]
     end
  
     def output_report
       puts('<html>')
       puts('  <head>')
       puts("    <title>#{@title}</title>")
       puts('  </head>')
       puts('  <body>')
       @text.each do |line|
         puts("    <p>#{line}</p>" )
       end
       puts('  </body>')
       puts('</html>')
     end
   end


Clearly, we are taking some liberties with this code in the interest of keeping this example simple. In real life, our report would not just be hard-coded into the class, and we certainly would not just include arbitrary text into an HTML file without checking for the odd "<" or ">". That said, the preceding code has some good things going for it. It is simple, it is easy to use, and it does produce HTML:


   report = Report.new
   report.output_report


If all you want to do is generate some basic HTML, this code or something like it is all you really need.

Keeping Up with What Life Throws at You

Unfortunately, even when life starts out simple, it rarely stays that way. Just months after you finished the preceding programming masterpiece, you get a new requirement: Your formatting object needs to produce plain text reports along with the current HTML. Oh, and we will probably need PostScript and maybe RTF output before the year is out.

Sometimes the simplest solutions are the best, so you just code your way around the problem in the dumbest possible way:



   class Report
     def initialize
       @title = 'Monthly Report'
       @text =  ['Things are going', 'really, really well.']
     end
  
     def output_report(format)
       if format == :plain
         puts("*** #{@title} ***")
       elsif format == :html
         puts('<html>')
         puts('  <head>')
         puts("    <title>#{@title}</title>")
         puts('  </head>')
         puts('  <body>')
       else
         raise "Unknown format: #{format}"
       end
  
       @text.each do |line|
         if format == :plain
           puts(line)
         else
           puts("    <p>#{line}</p>" )
         end
       end
  
       if format == :html
         puts('  </body>')
         puts('</html>')
       end
     end
   end


Yuk. This second version may work, but it is a mess. The code to handle the plain text formatting is tangled up with the HTML code. Worse, as you add more formats (remember that looming requirement for PostScript!), you will have to go back and rework the Report class to accommodate each new format. The way the code stands right now, each time you add a new format you risk breaking the code for the other formats. In short, our first attempt to add a new output format violates one of the guiding principles for design patterns: It mixes up code that is changing with code that is not changing.

Separate the Things That Stay the Same

The way out of this quandary is to refactor this mess into a design that separates the code for the various formats. The key in doing so is to realize that no matter which format is involved—whether plain text or HTML or the future PostScript—the basic flow of Report remains the same:

1. Output any header information required by the specific format.

2. Output the title.

3. Output each line of the actual report.

4. Output any trailing stuff required by the format.

With this sequence in mind, we can reach back to the lessons we learned in Object-Oriented Programming 101: Define an abstract base class with a master method that performs the basic steps listed above, but that leaves the details of each step to a subclass. With this approach, we have one subclass for each output format. Here is our new, abstract Report class:


   class Report
     def initialize
       @title = 'Monthly Report'
       @text =  ['Things are going', 'really, really well.']
     end
  
     def output_report
       output_start
       output_head
       output_body_start
       output_body
       output_body_end
       output_end
     end
  
     def output_body
       @text.each do |line|
         output_line(line)
       end
     end
  
     def output_start
       raise 'Called abstract method: output_start'
     end
     def output_head
       raise 'Called abstract method: output_head'
     end
  
     def output_body_start
       raise 'Called abstract method: output_body_start'
     end
  
     def output_line(line)
       raise 'Called abstract method: output_line'
     end
  
     def output_body_end
       raise 'Called abstract method: output_body_end'
     end
  
     def output_end
       raise 'Called abstract method: output_end'
     end
   end


Of course, this new Report class is not really an abstract class. While we might talk in theory about abstract methods and classes, the fact is that Ruby supports neither. The ideas of abstract methods and classes do not really fit with Ruby’s easygoing, dynamic view of life. The closest we can come is to raise exceptions should anyone try to call one of our “abstract” methods.

With our new Report implementation in hand, we can now define a Report subclass for each of our two formats. Here is the HTML class:


   class HTMLReport < Report
     def output_start
       puts('<html>')
     end
  
     def output_head
       puts('  <head>')
       puts("    <title>#{@title}</title>")
       puts('  </head>')
     end
  
     def output_body_start
       puts('<body>')
     end
  
     def output_line(line)
       puts("  <p>#{line}</p>")
     end
  
     def output_body_end
       puts('</body>')
     end
  
     def output_end
       puts('</html>')
     end
   end


Here is the plain text version:


   class PlainTextReport < Report
     def output_start
     end
  
     def output_head
       puts("**** #{@title} ****")
       puts
     end
  
     def output_body_start
     end
  
     def output_line(line)
       puts(line)
     end
  
     def output_body_end
     end
  
     def output_end
     end
   end


Using our new report classes is straightforward:


   report = HTMLReport.new
   report.output_report
  
   report = PlainTextReport.new
   report.output_report


Picking a format is now as easy as selecting the right formatting class.

Discovering the Template Method Pattern

Congratulations! You have just rediscovered what is probably the simplest of the original GoF patterns, the Template Method pattern.

As shown in Figure 3-1, the general idea of the Template Method pattern is to build an abstract base class with a skeletal method. This skeletal method (also called a template method) drives the bit of the processing that needs to vary, but it does so by making calls to abstract methods, which are then supplied by the concrete subclasses. We pick the variation that we want by selecting one of those concrete subclasses.

Figure 3-1. Class Diagram for the Template Method pattern

image

In our example, the basic outline is all the things you need to do to generate a report: output any header information, the report title, and then each line of the report. In this case, the detail-supplying methods of the subclasses deal with writing out the report in the correct format, either plain text or HTML. If we engineer all of these tasks correctly, we will end up separating the stuff that stays the same (the basic algorithm expressed in the template method) from the stuff that changes (the details supplied by the subclasses).

One characteristic that the HTMLReport and PlainTextReport classes share with all properly written Template Method pattern concrete subclasses is that they look fragmentary. Like the good concrete subclasses that they are, both HTMLReport and PlainTextReport override output_line and the other abstract methods. The subclasses get their fragmentary appearance from the fact that they do not override the key template method, output_report. In the Template Method pattern, the abstract base class controls the higher-level processing through the template method; the subclasses simply fill in the details.

Hook Methods

If you go back and look at PlainTextReport, you will see that while it does override the output_start and output_end methods as well as the start and end methods for the body, there is no actual code in any of the PlainTextReport versions of these methods. This is reasonable enough: Unlike an HTML document, a plain text document does not need any leading or trailing formatting. But there really is no reason to force a class such as PlainTextReport, which has no use for all of these start and stop methods, to define them anyway. It makes more sense for the base Report class to simply supply a default implementation of these methods for the convenience of its subclasses:


   class Report
     def initialize
       @title = 'Monthly Report'
       @text =  ['Things are going', 'really, really well.']
     end
  
     def output_report
       output_start
       output_head
       output_body_start
       output_body
       output_body_end
       output_end
     end
  
     def output_body
       @text.each do |line|
         output_line(line)
       end
     end
  
     def output_start
     end
  
     def output_head
       raise 'Called abstract method: output_head'
     end
  
     def output_body_start
     end
  
     def output_line(line)
       raise 'Called abstract method: output_line'
     end
  
     def output_body_end
     end
  
     def output_end
     end
   end


Non-abstract methods that can be overridden in the concrete classes of the Template Method pattern are called hook methods. Hook methods permit the concrete classes to choose (1) to override the base implementation and do something different or (2) to simply accept the default implementation. Frequently, the base class will define hook methods solely to let the concrete subclass know what is going on. When the Report class calls output_start, for example, it is telling its subclasses, “We are ready to start outputting the report, so if you need to do something, do it now.” The default implementations of these informative hook methods are frequently empty. They exist merely to let the subclasses know what is happening but do not require the subclasses to override methods that do not interest them.

Sometimes, however, the default implementation of a hook method may actually contain some code. In our Report example, we might default to treating the title like just another line of text:


   class Report
     def initialize
       @title = 'Monthly Report'
       @text =  ['Things are going', 'really, really well.']
     end
  
     def output_report
       output_start
       output_head
       output_body_start
       output_body
       output_body_end
       output_end
     end
  
     def output_body
       @text.each do |line|
         output_line(line)
       end
     end
  
     def output_start
     end
  
     def output_head
       output_line(@title)
     end
  
     def output_body_start
     end
  
     def output_line(line)
       raise 'Called abstract method: output_line'
     end
  
     def output_body_end
     end
  
     def output_end
     end
   end


But Where Are All the Declarations?

Given that this chapter describes our first Ruby pattern, it is worth taking a moment to consider the issues of types and type safety in Ruby. If you are recently arrived from the world of statically typed languages, you may be wondering how our Report class and its subclasses can get away with the almost total lack of declarations. Nowhere in the Report class, you may have noticed, do we declare that @title is a string or that @text is an array of strings. In the same vein, when our client code creates a new HTMLReport, we never actually say that the variable report holds a reference to an instance of HTMLReport or Report—it just does:

   report = HTMLReport.new

Ruby is dynamically typed, which means that the language does no checking to ensure that the objects being passed around have any particular class ancestry. The only thing that matters is that an object actually implements the methods that its clients want to call. In the preceding example, the Report class simply expects the @text object to behave like an array of strings. If @text looks like an array of strings—that is, if you can get the third string out of it with @text[2]—then whatever its actual class, it is the correct type.

This “I am what I am” approach to typing has been called duck typing. The name comes from the old bit of wisdom that goes, “If it looks like a duck and quacks like a duck, then it is a duck.” Another way to look at this issue is to think of static typing as working like an aristocracy: Statically typed languages are constantly asking about your parent or grandparent, or perhaps, in the case of Java-style interfaces, your aunts and uncles. In a statically typed language, an object’s family tree matters deeply. Dynamically typed languages, by contrast, are meritocracies: They are concerned with which methods you have, rather than where those methods came from. Dynamically typed languages rarely ask about an object’s ancestry; instead, they simply say, “I don’t care who you are related to, Mac. All I want to know is what you can do.”[1]

Types, Safety, and Flexibility

People who are used to programming in statically typed languages often wonder how all of this could possibly work. You might think that all of this free and easy duck typing stuff will certainly lead to disaster, with programs constantly crashing as they try to format some HTML with a database connection or attempt to tell the number 42 to generate a monthly report. Surprisingly, it turns out that these kinds of outrageous typing problems rarely occur.

You can find evidence of this robustness in the world of Java programs, of all places. Almost all Java programs written before the advent of Java 1.5—and that covers the bulk of existing Java programs—use the containers from the java.util package, things like HashMap and ArrayList. The pre-1.5 versions of these containers provided no type safety at all, and even post-1.5 Java continues to provide non-type-safe versions of these containers for backward compatibility. Despite this cavalier attitude toward type safety, most Java programs do not mix up their socket objects with their Employee objects and crash while trying to give a network connection a pay raise.

Statically typed languages are so pervasive these days that a key question is rarely asked: What is the cost of static typing? My answer is that static typing costs a lot. In fact, in the currency of programming effort and code clutter, static typing costs a fortune. Look at a Java or C# program and count the number of tokens devoted to parameter and variable declarations. Add in most of the interfaces. Don’t forget those pesky class casts, where you convince the typing system that, yes, that really is a String over there. Add a bonus for each complex generic declaration. All of this code clutter is not free.[2]

And it is not just programming effort. There is a very real, albeit hidden cost to static typing: It tends to couple your system together much more tightly than necessary. Consider the following Java isEmpty() method:


   public boolean isEmpty(String s)
   {
     return s.length() == 0;
   }


Now look at its Ruby twin:


   def empty?(s)
     s.length == 0
   end


On the surface, the two methods seem pretty much the same. Now consider that the Java version works only on arguments of type java.lang.String. The Ruby version will work on strings, to be sure—but it will also work with arrays, queues, sets, and hashes. In fact, the Ruby empty? method will work with any argument that has a length method. It doesn’t care what the exact type of the argument is, and perhaps it should not.

The arguments for dynamic typing might sound counterintuitive to the statically typed ear. If you are used to static typing, in which you declare everything all the time, it might seem unrealistic to suppose that you can build large, reliable systems without strong type checking. But it is possible—and there are two very obvious examples that demonstrate just how possible it is.

Ruby on Rails is by far the most prominent evidence that you can write reliable code in a dynamically typed language. Rails consists of tens of thousands of lines of dynamically typed Ruby code, and Rails is rock stable. If Rails does not persuade you, think about that other large lump of Ruby code in constant use everyday: the standard library that comes with Ruby. The Ruby standard library consists of more than 100,000 lines of Ruby. It is nearly impossible to write a Ruby program that does not use the Ruby standard library—and it works.

Ruby on Rails and the Ruby standard library are the existence proof: You can write large bodies of reliable code in a dynamically typed language. The fear that dynamic typing will produce code chaos is mostly unfounded. Yes, type problems do occasionally cause Ruby programs to crash. But Ruby programs do not crash with anything like the frequency that you might expect given the lengths that statically typed languages go to in to avoid even the remote possibility of type errors.

Does this mean that dynamically typed languages are just better, and that we should give up on statically typed languages completely? The jury is still out on this question. As with most software engineering questions, the answer involves seeing the two options as a balance. Static typing is probably worth the price on large, complex systems built by huge teams. But dynamic typing has some significant advantages: Programs written in Ruby are generally a fraction of the size of their statically typed equivalents. And, as we saw with the empty? example and shall see in the chapters to come, dynamic typing offers a huge flexibility bonus.

If all of this seems crazy to you, stick with me through the rest of this book, and give all of this dynamically typed insanity a chance. You may be pleasantly surprised.

Unit Tests Are Not Optional

One way that you can increase the chances that the surprise will be a pleasant one is to write unit tests. No matter whether you are writing in Java, C#, or Ruby, you should be writing unit tests: The second oldest[3] joke in programming is “It compiles, so it must work.”

While tests are important no matter which language you are using, they are critical when you are working in a dynamic language such as Ruby. There is no compiler in Ruby to make that first sanity check—and perhaps give you a false sense of security. Instead, the only way to know that the program is working is to run some tests. The good news is that the same unit tests that you need to show that your code works will also tend to ferret out the vast bulk of those pesky type-related problems.

The even better news is that if you know how to use JUnit, NUnit, or any of the other familiar XUnit-style libraries, then you already know how to write unit tests in Ruby. For example, the following class tests our Ruby empty? method:


   require 'test/unit'
   require 'empty'
  
   class EmptyTest < Test::Unit::TestCase
     def setup
       @empty_string = ''
       @one_char_string = 'X'
       @long_string = 'The rain in Spain'
  
       @empty_array = []
       @one_element_array = [1]
       @long_array = [1, 2, 3, 4, 5, 6]
     end
  
     def test_empty_on_strings
       assert empty?(@empty_string)
       assert ! empty?(@one_char_string)
       assert ! empty?(@long_string)
     end
  
     def test_empty_on_arrays
       assert empty?(@empty_array)
       assert ! empty?(@one_element_array)
       assert ! empty?(@long_array)
     end
   end


True to its XUnit roots, Test::Unit runs each of the methods whose name starts with test as a test. If your test class has a setup method (as the preceding class does), it is run before each test method. If your class has a teardown method (and the preceding class does not), it is run after each test method.

Test::Unit comes equipped with a whole menagerie of assert methods. You can assert that something is true, or you can assert_equal that two objects are equal. If you want to be sure that you have something and not nothing, you can assert_not_nil.

Running the unit tests could not be easier. If the test case above is found in a file called string_test.rb, then you can run the tests by simply executing the file as a Ruby program:


   $ ruby empty_test.rb
   Loaded suite empty_test
   Started
   ..
   Finished in 0.000708 seconds.
  
   2 tests, 6 assertions, 0 failures, 0 errors


Nothing like the feeling of a test that completes without complaint.

Using and Abusing the Template Method Pattern

Given that it is possible to write a reliable implementation of the Template Method pattern in Ruby, how do you actually go about doing so? The best approach is an evolutionary one: Start with one variation and simply code as though it was the only problem that you need to solve. In our report example, you might have started with HTML:


   class Report
     def initialize
       @title = 'MonthlyReport'
       @text =  ['Things are going', 'really, really well.']
     end
  
     def output_report
       puts('<html>')
       puts('  <head>')
       puts('    <title>#{@title}</title>')
       # output the rest of the report ...
     end
   end


Next, you could refactor the method that will become the template method so that it calls other methods for the variable parts of the algorithm, but still just focus on the one case:


   class Report
     # ...
  
     def output_report
       output_start
       output_title(@title)
       output_body_start
       for line in @text
         output_line(line)
       end
       output_body_end
       output_end
     end
  
     def output_start
       puts('<html>')
     end
  
     def output_title(title)
       puts('  <head>')
       puts("    <title>#{title}</title>")
       puts('  </head>')
     end
  
     # ...
  
   end


Finally, you could create a subclass for your first case and move your specific implementation into that subclass. At this point, you are ready to start coding the rest of the variations.

As mentioned in Chapter 1, the worst mistake you can make is to overdo things in an effort to cover every conceivable possibility. The Template Method pattern is at its best when it is at its leanest—that is, when every abstract method and hook is there for a reason. Try to avoid creating a template class that requires each subclass to override a huge number of obscure methods just to cover every conceivable possibility. You also do not want to create a template class that is encrusted with a multitude of hook methods that no one will ever override.

Templates in the Wild

You can find a classic example of the Template Method pattern in WEBrick, the lightweight, all-Ruby library for building TCP/IP servers. A key part of WEBrick is the GenericServer class, which implements all of the details of being a network server. Of course, GenericServer has no idea what you want to actually accomplish in your server. Thus, to use GenericServer, you simply extend it and override the run method:


   require 'webrick'
  
   class HelloServer < WEBrick::GenericServer
     def run(socket)
       socket.print('Hello TCP/IP world')
     end
   end


The template method buried inside GenericServer contains all the code for listening on a TCP/IP port, accepting new connections, and cleaning up when a connection is broken. And right in the middle of all that code, right when it has its hands on a new connection, it calls your run method.[4]

There is another very common example of the Template Method pattern that is perhaps so pervasive that it is hard to see. Think about the initialize method that we use to set up our objects. All we know about initialize is that it is called sometime toward the end of the process of creating a new object instance and that it is a method that we can override in our class to do any specific initialization. Sounds like a hook method to me.

Wrapping Up

In this chapter we looked in detail at our first pattern, the Template Method pattern. The Template Method pattern is simply a fancy way of saying that if you want to vary an algorithm, one way to do so is to code the invariant part in a base class and to encapsulate the variable parts in methods that are defined by a number of subclasses. The base class can simply leave the methods completely undefined—in that case, the subclasses must supply the methods. Alternatively, the base class can provide a default implementation for the methods that the subclasses can override if they want.

Given that this was our first pattern, we also took a bit of a detour in which we explored one of the most important aspects of programming in Ruby: duck typing. Duck typing is a trade-off: You give up the compile-time safety of static typing, and in return you get back a lot of code clarity and programming flexibility.

Looking ahead, we will soon see that the Template Method pattern is such a basic object-oriented technique that it pops up in other patterns. In Chapter 13, for example, we will learn that the Factory Method pattern is simply the Template Method pattern applied to creating new objects. The problem that the Template Method pattern attacks is also reasonably pervasive. In fact, in the next chapter we will look at the Strategy pattern, which offers a different solution to the same problem—a solution that does not rely on inheritance in the same way that the Template Method pattern does.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.117.75