Chapter 10. The Abstract Factory Pattern

The Abstract Factory pattern is one level of abstraction higher than the Factory pattern. You can use this pattern when you want to return one of several related classes of objects, each of which can return several different objects on request. In other words, the Abstract Factory is a factory object that returns one of several groups of classes. You might even decide which class to return from that group using a Simple Factory.

Common thought experiment–style examples might include automobile factories. You would expect a Toyota factory to work exclusively with Toyota parts and a Ford factory to use Ford parts. You can consider each auto factory as an Abstract Factory and the parts the groups of related classes.

A GardenMaker Factory

Let’s consider a practical example where you might want to use the abstract factory in your application. Suppose you are writing a program to plan a garden design. This could include annual, vegetable, or perennial gardens. However, no matter which kind of garden you are planning, you will have the same questions.

  1. What are good border plants?
  2. What are good center plants?
  3. What plants do well in partial shade?

(You would probably have a lot more plant questions, but we won’t get into them here.)

We want a base C# Garden class that can answer these questions as class methods.


public class Garden {
      protected Plant center, shade, border;
      protected bool showCenter, showShade, showBorder;
      //select which ones to display
      public void setCenter() {showCenter = true;}
      public void setBorder() {showBorder =true;}
      public void setShade() {showShade =true;}
      //draw each plant
      public void draw(Graphics g) {
             if (showCenter) center.draw (g, 100, 100);
             if (showShade) shade.draw (g, 10, 50);
             if (showBorder) border.draw (g, 50, 150);
      }
}

Our Plant object sets the name and draws itself when its draw method is called.


public class Plant {
      private string name;
      private Brush br;
      private Font font;

      public Plant(string pname) {
             name = pname;     //save name
             font = new Font ("Arial", 12);
             br = new SolidBrush (Color.Black );
      }
      //-------------
      public void draw(Graphics g, int x, int y) {
             g.DrawString (name, font, br, x, y);
      }
}

In Design Patterns terms, the Garden interface is the Abstract Factory. It defines the methods of concrete class that can return one of several classes. Here, we return central, border, and shade-loving plants as those three classes. The abstract factory could also return more specific garden information, such as soil pH or watering requirements.

In a real system, each type of garden would probably consult an elaborate database of plant information. In our simple example we’ll return one kind of each plant. So, for example, for the vegetable garden we simply write the following.


public class VeggieGarden : Garden {
      public VeggieGarden() {
             shade = new Plant("Broccoli");
             border = new Plant ("Peas");
             center = new Plant ("Corn");
      }
}

In a similar way, we can create Garden classes for PerennialGarden and AnnualGarden. Each of these concrete classes is known as a Concrete Factory, since it implements the methods in the parent class. Now we have a series of Garden objects, each of which creates one of several Plant objects. This is illustrated in the class diagram in Figure 10-1.

Figure 10-1. The major objects in the Gardener program

image

We can easily construct our Abstract Factory driver program to return one of these Garden objects based on the radio button that a user selects, as shown in the user interface in Figure 10-2.

Figure 10-2. The user interface of the Gardener program

image

Each time you select a new garden type, the screen is cleared and the check boxes unchecked. Then, as you select each checkbox, that plant type is drawn in.

Remember that in C# you do not draw on the screen directly from your code. Instead, the screen is updated when the next paint event occurs, and you must tell the paint routine what objects to paint.

Since each garden (and Plant) knows how to draw itself, it should have a draw method that draws the appropriate plant names on the garden screen. And since we provided check boxes to draw each of the types of plants, we set a Boolean that indicates that you can now draw each of these plant types.

Our Garden object contains three set methods to indicate that you can draw each plant.


public void setCenter() {showCenter = true;}
public void setBorder() {showBorder =true;}
public void setShade() {showShade =true;}

The PictureBox

We draw the circle representing the shady area inside the PictureBox and draw the names of the plants inside this box as well. This is best accomplished by deriving a new GardenPic class from PictureBox and giving it the knowledge to draw the circle and the garden plant names. Thus, we need to add a paint method not to the main GardenMaker window class but to the PictureBox it contains. This overrides the base OnPaint event of the underlying Control class.


public class GdPic : System.Windows.Forms.PictureBox  {
      private Container components = null;
      private Brush br;
      private Garden gden;
      //-----------
      private void init () {
             br = new SolidBrush (Color.LightGray );
      }
      //-----------
      public GdPic()      {
             InitializeComponent();
             init();
      }
      //-----------
      public void setGarden(Garden garden) {
             gden = garden;
      }
      //-----------
      protected override void OnPaint ( PaintEventArgs pe ){
             Graphics g = pe.Graphics;
             g.FillEllipse (br, 5, 5, 100, 100);
             if(gden != null)
                    gden.draw (g);
      }

Note that we do not have to erase the plant name text each time because OnPaint is only called when the whole picture needs to be repainted.

Handling the RadioButton and Button Events

When one of the three radio buttons is clicked, you create a new garden of the correct type and pass it into the picture box class. You also clear all the check boxes.


private void opAnnual_CheckedChanged(
             object sender, EventArgs e) {
      setGarden( new AnnualGarden ());
}
//-----
private void opVegetable_CheckedChanged(
             object sender, EventArgs e) {
      setGarden( new VeggieGarden ());
}
//-----
private void opPerennial_CheckedChanged(
             object sender, EventArgs e) {
      setGarden( new PerennialGarden ());
}
//-----
private void setGarden(Garden gd) {
      garden = gd;                //save current garden
      gdPic1.setGarden ( gd);     //tell picture bos
      gdPic1.Refresh ();          //repaint it
      ckCenter.Checked =false;    //clear all
      ckBorder.Checked = false;   //check
      ckShade.Checked = false;    //boxes
}

When you click on one of the check boxes to show the plant names, you simply call that garden’s method to set that plant name to be displayed and then call the picture box’s Refresh method to cause it to repaint.


private void ckCenter_CheckedChanged(
             object sender, System.EventArgs e) {
      garden.setCenter ();
      gdPic1.Refresh ();
}
//-----
private void ckBorder_CheckedChanged(
             object sender, System.EventArgs e) {
      garden.setBorder();
      gdPic1.Refresh ();
}
//-----

private void ckShade_CheckedChanged(
             object sender, System.EventArgs e) {
      garden.setShade ();
      gdPic1.Refresh ();
}

Adding More Classes

One of the great strengths of the Abstract Factory is that you can add new subclasses very easily. For example, if you needed a GrassGarden or a Wild-FlowerGarden, you can subclass Garden and produce these classes. The only real change you’d need to make in any existing code is to add some way to choose these new kinds of gardens.

Consequences of Abstract Factory

One of the main purposes of the Abstract Factory is that it isolates the concrete classes that are generated. The actual class names of these classes are hidden in the factory and need not be known at the client level at all.

Because of the isolation of classes, you can change or interchange these product class families freely. Further, since you generate only one kind of concrete class, this system keeps you from inadvertently using classes from different families of products. However, it is some effort to add new class families, since you need to define new, unambiguous conditions that cause such a new family of classes to be returned.

While all of the classes that the Abstract Factory generates have the same base class, there is nothing to prevent some subclasses from having additional methods that differ from the methods of other classes. For example, a BonsaiGarden class might have a Height or WateringFrequency method that is not in other classes. This presents the same problem that occurs in any subclass: You don’t know whether you can call a class method unless you know whether the subclass is one that allows those methods. This problem has the same two solutions as in any similar case: You can either define all of the methods in the base class, even if they don’t always have an actual function, or you can derive a new base interface that contains all the methods you need and subclass that for all of your garden types.

Thought Question

If you are writing a program to track investments, such as stocks, bonds, metal futures, derivatives, and the like, how might you use an Abstract Factory?

Program on the CD-ROM

image

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

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