Chapter 24. The Strategy Pattern

The Strategy pattern is much like the State pattern in outline but a little different in intent. The Strategy pattern consists of a number of related algorithms encapsulated in a driver class called Context. Your client program can select one of these algorithms, or in some cases Context might select the best one for you. The intent is to make these algorithms interchangeable and provide a way to choose the most appropriate one. The State and Strategy differ in that the user generally chooses which of several strategies to apply, and only one strategy at a time is likely to be instantiated and active within the Context class. By contrast, all of the different states could possibly be active at once, and switching between them might occur often. In addition, Strategy encapsulates several algorithms that do more or less the same thing, while State encapsulates related classes that each do something somewhat different. Finally, the concept of transition between different states is completely missing in the Strategy pattern.

Motivation

A program that requires a particular service or function and that has several ways of carrying out that function is a candidate for the Strategy pattern. Programs choose between these algorithms based on computational efficiency or user choice. There may be any number of strategies, more can be added, and any can be changed at any time.

Often, you want to do the same thing in several different ways, such as the following, mentioned in Smalltalk Companion:

  • Save files in different formats.

  • Compress files using different algorithms.

  • Capture video data using different compression schemes.

  • Use different line-breaking strategies to display text data.

  • Plot the same data in different formats: line graph, bar chart, or piechart.

In each case, the client program could tell a driver module (Context) which of these strategies to use and then ask it to carry out the operation.

The idea behind Strategy is to encapsulate the various strategies in a single module and provide a simple interface to allow a choice between them. Each strategy must have the same programming interface, but they do not need to be members of the same class hierarchy. However, they do have to implement the same programming interface.

Sample Code

Let's consider a simplified graphing program that can present data as a line graph or a bar chart. We start with an abstract PlotStrategy class and derive the two plotting classes from it, as illustrated in Figure 24.1.

Two instances of a PlotStrategy Class.

Figure 24.1. Two instances of a PlotStrategy Class.

Each plot will appear in its own frame, so the base PlotStrategy class will be derived from JFrame.

public abstract class PlotStrategy extends JFrame {
    protected float[]    x, y;
    protected float      minX, minY, maxX, maxY;
    protected int        width, height;
    protected Color      color;

    public PlotStrategy(String title) {
        super(title);
        width = 300;
        height = 200;
        color = Color.black; 
    addWindowListener(new WindAp(this));
    }
    //--------------
    public abstract void plot(float xp[], float yp[]);

    //--------------
    public void setSize(Dimension sz) {
        width = sz.width;
        height = sz.height;
    }
    //--------------
    public void setPenColor(Color c) {
        color = c;
    }

The important part is that all of the derived classes must implement a method called plot with two Float arrays as arguments. Each of these classes can do any kind of plot that is appropriate.

Note that we don't derive it from the special JxFrame class because we don't want the entire program to exit if we close one of these subsidiary windows. Instead, we add a WindowAdapter class that hides the window if it is closed.

public class WindAp extends WindowAdapter {
    JFrame fr;
    public WindAp(JFrame f) {
        fr = f;           //copy the Jframe instance
    }
    public void windowClosing(WindowEvent e) {
        fr.setVisible(false);   //hide the window
    }
}

The Context Class

The Context class is the traffic cop that decides which strategy is to be called. The decision is usually based on a request from the client program, and all that Context needs to do is to set a variable to refer to one concrete strategy or another.

public class Context {
    //this object selects one of the strategies
    //to be used for plotting
    private PlotStrategy plotStrategy;
    private float x[], y[];
//--------------
    public Context() {
        setLinePlot();
}
public void setBarPlot() {
    plotStrategy = new BarPlotStrategy();
}
public void setLinePlot() {
    plotStrategy = new LinePlotStrategy();
}
//--------------
    public void plot() {
        plotStrategy.plot(x, y);
    }
//--------------
    public void setPenColor(Color c) {
       plotStrategy.setPenColor(c);
    }
//--------------
public void readData(String filename) {
}

The Context class is also responsible for handling the data. Either it obtains the data from a file or database, or the data are passed in when the Context is created. Depending on the magnitude of the data, they can either be passed on to the plot strategies or the Context can pass an instance of itself into the plot strategies and provide a public method to fetch the data.

The Program Commands

The program, shown in Figure 24.2, is a panel with two buttons that call the two different plots.

A panel to call two different plots.

Figure 24.2.  A panel to call two different plots.

Each button is a Command object that sets the correct strategy and then calls Context's plot routine. For example, following is the complete Line graph Button class:

public class JGraphButton extends JButton implements Command {
    Context context;
    public JGraphButton(ActionListener act, Context ctx) {
        super("Line graph");
        addActionListener(act);
        context  = ctx;
    }
    //--------------
    public void Execute() {
        context.setPenColor(Color.red);    //set the color of the 
                                             plot
        context.setLinePlot();             //set the kind of the 
                                             plot
        context.readData("data.txt");      //read the data
        context.plot();                    //plot the data
    }
}

The Line and Bar Graph Strategies

The two Strategy classes are pretty much the same: They set up the window size for plotting and call a plot method specific for that display panel. Here is the Line graph Strategy:

public class LinePlotStrategy extends PlotStrategy {
    private LinePlotPanel lp;

    public LinePlotStrategy() {
        super("Line plot");
        lp = new LinePlotPanel();
        getContentPane().add(lp);
    }
//--------------
    public void plot(float[] xp, float[] yp) {
        x = xp; y = yp;            //copy in data
        findBounds();              //sets maxes and mins
        setSize(width, height);
        setVisible(true);
        setBackground(Color.white);
        lp.setBounds(minX, minY, maxX, maxY);
        lp.plot(xp, yp, color);    //set up the plot data
        repaint();                 //call paint to plot
    }
}

Drawing Plots in Java

The Java GUI is event-driven, so we don't actually write a routine that draws lines on the screen in the direct response to the plot command event. Instead, we provide a panel whose paint event carries out the plotting when that event is called. The previous repaint method ensures that it will be called right away.

We create a PlotPanel class based on JPanel and derive two classes from it for the actual line and bar plots (Figure 24.3).

The two plot Panel classes derived from PlotPanel.

Figure 24.3. The two plot Panel classes derived from PlotPanel.

The base PlotPanel class contains the common code for scaling the data to the window.

public class PlotPanel extends JPanel {
    private float xfactor, yfactor;
    private int   xpmin, ypmin, xpmax, ypmax;
    private float minX, maxX, minY, maxY;
    private float x[], y[];
    private Color color;
//--------------
    public void setBounds(float minx, float miny, float maxx,
            float maxy) {
        minX = minx;
        maxX = maxx;
        minY = miny;
        maxY = maxy;
    }
//--------------
    public void plot(float[] xp, float[] yp, Color c) {
        x =    xp;        //copy in the arrays
        y =    yp;
        color = c;        //and color

        //compute bounds and scaling factors
        int w = getWidth() - getInsets().left - getInsets().right;
        int h = getHeight() - getInsets().top -
                    getInsets().bottom;

        xfactor = (0.9f * w) / (maxX - minX);
        yfactor = (0.9f * h)/ (maxY - minY);

        xpmin = (int) (0.05f * w);
        ypmin = (int) (0.05f * h);
        xpmax = w - xpmin;
        ypmax = h - ypmin;
        repaint();      //this causes the actual plot
   }
//--------------
    protected int calcx(float xp) {
        return(int) ((xp-minX) * xfactor + xpmin);
    }
//--------------
    protected int calcy(float yp) {
        int ypnt = (int)((yp-minY) * yfactor);
        return ypmax - ypnt;
    }
}

The two derived classes implement the paint method for the two kinds of graphs. Here is the one for the Line plot:

public class LinePlotPanel extends PlotPanel {

    public void paint(Graphics g) {
        Super.paint(g);
        int xp = calcx(x[0]);          //get the first point
        int yp = calcy(y[0]);
        g.setColor(Color.white);       //flood the background
        g.fillRect(0,0,getWidth(),     getHeight());
        g.setColor(Color.black);

        //draw bounding rectangle
        g.drawRect(xpmin, ypmin, xpmax, ypmax);
        g.setColor(color);

        //draw line graph
        for (int i = 1; i < x.length; i++) {
            int xp1 = calcx(x[i]);
            int yp1 = calcy(y[i]);
            g.drawLine(xp, yp, xp1, yp1);
            xp = xp1;
            yp = yp1;
        }
    }
}

The final two plots are shown in Figure 24.4.

The line graph (left) and the bar graph (right).

Figure 24.4. The line graph (left) and the bar graph (right).

The class diagram is given in Figure 24.5.

The UML class diagram for the PlotStrategy classes. Note that the Command pattern is again used.

Figure 24.5. The UML class diagram for the PlotStrategy classes. Note that the Command pattern is again used.

PlotStrategy is the abstract class, and BarPlotStrategy and LinePlotStrategy are subclasses. The containers BarPlotPanel and LinePlotPanel contain instances of these two classes. The Context class contains code to select a strategy based on the request from either of the two Command buttons.

Consequences of the Strategy Pattern

The Strategy pattern allows you to select one of several algorithms dynamically. These algorithms may be related in an inheritance hierarchy, or they may be unrelated as long as they implement a common interface. Since the Context switches between strategies at your request, you have more flexibility than if you simply call the desired derived class. This approach also avoids the sort of condition statements that can make code hard to read and maintain.

However, Strategies don't hide everything. The client code is usually aware that there are a number of alternative strategies, and it has some criteria for choosing from among them. This shifts an algorithmic decision to the client programmer or the user.

You could pass any number of different parameters to different algorithms, so you must develop a Context interface and strategy methods that are broad enough to allow for passing in parameters that are not used by that particular algorithm. For example, the setPenColor method in PlotStrategy is actually used only by the LineGraph strategy. The BarGraph strategy ignores that method, since it sets up its own list of colors for the successive bars that it draws.

Thought Question

  1. Use the Strategy pattern in a program to read in text and display it in a JTextField either in a monospaced font without wrapping or in a proportional font with wrapping set.

Programs on the CD-ROM

Program Description

StrategyStrategyPlot.java

Selects algorithms for drawing data as line or bar plots.
..................Content has been hidden....................

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