Copyright © 2012 David Schmidt

Lecture 11:
Design Patterns I: Factories and Iterators


11.1 Factory method
11.2 Iterator
11.3 Singleton class
11.4 Abstract factory
11.5 Summary



11.1 Factory method

A method that returns as its answer the handle to a newly constructed object is called a factory method --- the method "manufactures" an object each time it is called. We saw an example in the previous lecture in the Manager/Helper example.

Here is an example that we will develop in detail. We did a VS exercise where two threads of execution shared a "file" for reading and writing. The controller ensured that the file was never simultaneously used for both reading and writing. The example was structured like this:

The code in FileController remembered whether the TextFile was being used for reading or writing and used this information to do the reads and writes.

It is safe for multiple threads to read the same file simultaneously, and the threads can even read at different rates of speed, since no thread changes the file. We should modify the above design so that it allows multiple reader threads. How is the controller changed to handle multiple readers?

In our original VS solution, the controller remembered which line the reader thread was reading. It's too complicated for the controller to track the progress of multiple reader threads --- it's better to manufacture, for each reader thread, an object that remembers the progress of the reader thread. We use a factory method, makeReader(...), to manufacture TextFileReader objects:

When each ReaderForm thread wants to read the file, it calls the FileController's openRead() method, which checks if the status is Available or Reading. In these cases, the status is set to Reading, the numberOfReaders is increased by one, and a thefile.makeReader(...) manufactures a FileReader object. The object's handle is returned to the ReaderForm, which calls the object's read method to read the file's lines, one at a time. (The FileReader object holds an internal counter to remember how far the file has been read. Once the file is completely read, the close method is called to tell the controller that the reading is finished.)

The makeReader is a factory method --- it manufactures FileReaders.

When there are multiple ReaderForm threads, each one calls openRead() and obtains its own manufactured FileReader for reading the shared file.

The FileController is programmed to allow a WriterForm thread to write to the file only when the file's status is Available. (Then, the status is set to Writing, and the file can be written.) In the next section, we see how to define class FileWriter to do file writing.

Here are the relevant classes:

===================================================

// defines the controller's state:
public enum Mode {Available, Read, Write};

// controls access to a "File" that can be read or written
public class FileController { 
      private TextFile thefile;  // handle to the file that is controlled
      private Mode status = Mode.Available;  // current mode of use 
      private int readers = 0;               // number of active readers 
         
      public FileController() { }

      // allows multiple readers;  returns handle to new FileReader
      // If file is busy writing, then returns null.
      public TextFileReader openRead() {
      lock(this){
          TextFileReader r = null;
          if (status == Mode.Available || status == Mode.Read) {
              status = Mode.Read;
              readers = readers + 1;
              r = thefile.makeReader(closeRead);  // see class TextFile below
          }
          return r;
      }}

      // closes file
      public void closeRead() {
      lock(this){
          readers = readers - 1;
          if (readers == 0 ) { status = Mode.Available; }
      }}

      // opens for writing; returns FileWriter. If file busy reading, returns null.
      public TextFileWriter openWrite() {
      lock(this){
          TextFileWriter w = null;
          if (status == Mode.Available) { 
                status = Mode.Write;
                w = thefile.makeWriter(closeWrite);
          }
          return w;
      }}

      // closes file and resets mode to Mode.Available
      public void closeWrite() {
      lock (this) { status = Mode.Available; }
     }
}

===================================================
===================================================

// delegate that defines type of method for closing file:
public delegate void CloseOperation();

public class TextFile {
    private List<string> contents;

    public TextFile() { reset(); }
    public void reset() { contents = new List<string>(); }
    public Tuple<bool, string> readAt(int i) {
        String s = "EOF";   bool outcome = false;
        if ( 0 <= i && i < contents.Count) { 
            return new Tuple<bool, string>(true, contents.ElementAt(i)); }
        else { return new Tuple<bool, string>(false, "EOF"); }
    }
    public bool write(string s) {  contents.Add(s); }
 
    // factory method:
    public TextFileReader makeReader(CloseOperation c) {
        return new TextFileReader(this, c); 
    }

    // factory method:
    public TextFileWriter makeWriter(CloseOperation c) {
        reset();
        return TextFileWriter.newWriter(this, c); 
    }

===================================================
===================================================

// fields and methods for reading a shared textfile, one line at a time:
public class TextFileReader {
        private Textfile myfile;  // handle to the file to be read
        private int count;        // how many lines in file have been read
        private CloseOperation closefile;  // method that closes the file

        public FileReader(TextFile t, CloseOperation c) {
            myfile = t;  count = 0;  closefile = c;  
        }

        // checks if the file has more lines to read
        public bool more() {
            return (count < myfile.Count);
        }

        // reads and returns next line of file.  Returns "EOF" if no more lines.
        public string read() {
            string line = "EOF";
            var pair = myfile.readAt(count);
            if (pair.Item1) { // did read succeed?
                line = pair.Item2
                count = count + 1;
            }
            return line;
        }

        // closes file once reading finished
        public void close() {
            closefile();
        }
}

===================================================

Summary

The simplest version of the Factory Method Pattern looks like this:

If you study Dr. Mizuno's notes and the Design Patterns book, you will see interfaces included, because this makes the pattern more general. (We will add interfaces, as well, below.)

Here is a side issue: when a Client is finished using the manufactured object, it might wish to "dispose" of it. The way to do this in C# is to call the object's Finalize() method and then erase all occurrences of its handle. Then, the background garbage-collector program will erase the unneeded object from heap storage.


11.2 Iterator

In the above example, the FileReader's read method counts through the file's lines, one at a time, meaning that the ReaderForm and the FileController don't do the counting.

An object that "counts through" the elements of a collection is called an iterator object. In the above example, each FileReader object is an iterator for the file.

An iterator object must have a method that returns the next item in a collection and a method that asks if there are any more items left to be returned. You can use an iterator object like FileReader with a loop like this:

TextFile file = ...;
TextFileReader r = controller.openRead();
if (r != null) {  // OK to read?
    while (r.more()) {  // reading lines from file, one at a time:
         string s = r.read();
         Console.WriteLine(s);
    }
}
The example looks like the iterator objects used in Java. In the C# .NET library, there is an interface, IEnumerable, that defines how you are supposed to code an interator class in C#:
===================================================

public interface IEnumerable {
  // moves to the next element in the collection and makes it Current;
  // returns true if successful;  returns false if there is no next element.
  public bool MoveNext(); 

  // resets the counting to the front of the collection:
  public void Reset();

  // returns the value of the current element in the collection:
  object IEnumerator.Current;
}

===================================================
You are also supposed to write a factory method, GetEnumerator(), which constructs the iterator object. Here is a small example:
===================================================

// defines my collection of objects built from  class C:
class MyCollection {
   // holds data structure of objects constructed from class C:
   C[] obs = ...;

   public IEnumerable getEnumerator() {
       return new CollectionIterator(obs);
   }

class CollectionIterator: IEnumerable {
  private MyCollection c;  // handle to the data structure to be iterated
  private int counter i;

  public CollectionIterator(C[] obs) { c = obs;  Reset(); }

  public bool MoveNext() {
     bool answer = false;
     if ((i+1) < c.Count ) { i = i + 1;  answer = true; }
     return answer;
  }
  public void Reset() { i = -1; }
  public C Current() { return c[i]; }
  object IEnumerator.Current { get{ this.Current() }; }
}

===================================================
The C# compiler lets you use the objects that implement the IEnumerable interface with a foreach loop. Indeed, a foreach loop like this one:
MyCollection collection = ... ;
foreach (C c in collection) {
   ... c ...
}
is reformatted by the C# compiler into this while loop that uses the iterator object:
IEnumerable iterator = collection.GetEnumerator();
iterator.Reset();
while (iterator.MoveNext()) {
    C c =  (C)(iterator.Current);
    ... c ...
}
Dr. Mizuno's CIS501 notes have several good examples of iterators in Java and C#.

Whenever you define a data structure that holds a collection of objects --- a matrix, a tree, a linked list, etc. --- you should define an iterator that can traverse the structure and enumerate the objects. Then you can use the foreach loop to process the data structure.

Summary

The simplest form of "iterator pattern" looks like this:


(If you check Dr. Mizuno's notes or the Design Patterns book, you will see an interface in the pattern to make it more general purpose.)

The AggregateIterator holds the algorithm for traversing the ItemAggregate, one Item at a time. This simplifies the coding of the latter and also makes possible multiple active iterators. On the negative side, the AggregateIterator holds only a handle to the ItemAggregate (and not a copy of all the Items in it). Therefore, it is dangerous and probably erroneous to change the contents of the ItemAggregate while an AggregateIterator is in use!


11.3 Singleton class

In the above example, class FileReader does the work of reading the file; this simplifies class FileController. We want a class FileWriter that does the same form of work. But only one thread at any time can write to the file, so there should be at most one object constructed from class FileWriter. A class from which we build exactly one object is a singleton class.

Here is the new class, class FileWriter, written so that there is only one object constructed from it. (The key is the private constructor method!)

===================================================

// constructs a single object for writing to a text file (list of strings)
public class TextFileWriter
   // holds the handle to the "singleton" FileWriter object:
   private static TextFileWriter writerOb = new TextFileWriter();
   private static bool inUse;  // remembers if  writerOb  is being used

   private static Textfile myfile;          // the file to write to
   private static CloseOperation closefile; // method that closes the file

   // the constructor is private !
   private TextFileWriter() { inUse = false; }

   public void write(string s) { myfile.write(s); }
   public void close() { inUse = false;  closefile(); }

   // returns the handle to the FileWriter object, if it isn't inUse
   public static TextFileWriter newWriter(TextFile f, CloseOperation c) {
        TextFileWriter w = null;
        if (!inUse) {
             inUse = true;  w = writerOb;
             closefile = c;
             myfile = f;  myfile.reset();
        }
        return w;
   }
}

===================================================
You use the singleton class like this:
TextFileWriter mywriter = TextFileWriter.newWriter(...,...);
It is illegal to say
TextFileWriter mywriter = new TextFileWriter()
because the constructor is private.


11.4 Abstract factory

Thanks to the factory methods, the FileController in the above system is greatly simplified. It now has one job only --- to enforce the protocol of file usage. This is the true job of the controller.

We see that the FileController can be used to control other kinds of resources besides textfiles. Maybe we use it with binary files or with a shared data buffer or with a hardware device with readable/writable data.

We can "cut" the above design into two, to expose how the FileController might connect to other resources. Here is the controller's part, where we insert interfaces on the outgoing arcs:




The interfaces are "plug-in" points for the file and the forms of reader/writer. When we replug-in the classes we coded so far, we have the system we started with:



But we can reuse the controller with other forms of file:

When there are a set of interfaces that include factory methods, this is called an abstract factory. We plug into the interfaces a "concrete factory" of classes that "manufacture" a family of objects that form a subassembly. In the above example, we have a simple abstract factory for manufacturing readers and writers. There are two concrete factories --- one for text files and one for binary files.

Summary

The best-known example of an abstract factory is a collection of factory methods and interfaces for manufacturing graphics. We might have a set of Windows XP widget classes, a set of Windows 7 widget classes, and a set of Mac widget classes, all implementing an abstract factory of widget interfaces.

Here is a page from Design Patterns, by E. Gamma, et al. (Addison Wesley, 1995, copied under "fair use" laws) that explains the example well:


11.5 Summary

  1. A factory method is a method that, when called, "manufactures" (constructs) a new "helper" object and returns the handle of the helper object as its answer. A client object calls the factory method to obtain a helper to do computation.

  2. An iterator object is an object that enumerates (returns, one by one, one at a time) the elements of a data-structure (model). The iterator object hides the traversal algorithm that traverses the data structure and extracts the elements.

  3. A singleton class is a class designed to construct at most one object. It is implemented with a private constructor method and a static method that returns the handle to the one-and-only object constructed from calling the private constructor method one time.

  4. An abstract factory is a collection of interfaces for factory methods and classes. A "concrete factory" of factory methods and classes plug-into (implement) the abstract factory. The concrete factory manufactures a family of objects that belong together as a subassembly, e.g., a family of GUI widgets.