We will start from a high level and think about how we would like to be able to use such a utility class. Ideally it would be similar to other types of graphs classes. To keep things simple we want the following parameter to be adjustable
The min and max x- and y-axis scale values to be displayed in the graph
The limits for the color indications, i.e. exactly what is the green, yellow, red and brown areas should be customizable. In oder to keep the interface simple we assume that the areas are bounded by straight lines and that we want to be able to specify the areas by stating the value at x=0 and the value at x=maximum x-scale
So for example the standard green area is limited by the specification (5,75) meaning that the green line crosses y=5 at x=0 and y=75 at x=100
We would also like to be ale to adjust the colors used as indicators
We want to be able to add both scatter marks and lines (to show history)
We also need to be able to specify the title and the sub title
The rest of the graph options and setting we would like the utility class to handle for us. As a bonus it would be nice if the class adjusted the size of the fonts to the overall size we have specified for the graph automatically to make it look visually proportional.
When doing designs like this it is always good to first write a small test driver without having to think about how to actually implement it. This keeps an end user perspective on things. Later on we can do changes if it turns out that a particular simplification for the user is simply too complex to implement.
So, lets think about how we (ideally) would like to be able to create the chart shown in Figure 31.3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $graph = new CCBPGraph(300,250); $graph->SetTitle('Buffer penetration','(history)'); $datax=array(83); $datay=array(60); $datax_history = array(0,17,17,17,33,50,67,83); $datay_history = array(0,20,40,60,60,60,60,60); $sp = new ScatterPlot($datay,$datax); $sp->mark->SetType(MARK_DIAMOND); $sp->mark->SetFillColor('white'); $sp->mark->SetSize(10); $sp_hist = new LinePlot($datay2,$datax2); $sp_hist->SetWeight(2); $sp_hist->SetColor('black'); $graph->Add($sp_hist); $graph->Add($sp); $graph->Stroke(); |
We cannot hope to have the class make it any simpler than this. This is as close to a "normal" line graph we can get (apart from the color indications in the background). So now its just a matter on designing this class.
Since the utility class will be a rather restricted graph class it with a very specific usage it doesn't make sense to implement it as an extension to the normal graph. Instead we will create contain class that contains a graph that we can modify and setup. (In UML language a has-a relation.)
We will name our utility class CCBPGraph
(Short for Critical chain Buffer Penetration Graph). The class
should have the following signature
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class CCBPGraph{ private $graph = null ; // The real graph instance // The "normal" constructor public function __construct($aWidth,$aHeight) {} // Methods that will control the appearance public function SetTitle($aTitle,$aSubTitle) {} public function SetXMinMax($aMin,$aMax) {} public function SetYMinMax($aMin,$aMax) {} public function SetColorIndication($aSpec,$aColors=null) {} public function SetColorMap($aMap) {} // Internal function to create and setup the graph private function Init() {} // Mimix the standard graph functions Add() and Stroke() public function Add($aPlots) {} public function Stroke($aFile='') {} } |
In addition the class must have a number of instance variables that are used to store the settings until we need them when we create the graph. However to keep things simple here we do not list them above. Lets now walk through the four groups of methods.
The constructor
public function __construct($aWidth,$aHeight) {}
The constructor doesn't need to do very much. It just needs to store the user specified width and height so we get them back when we create the actual graph. We could also make use of the constructor to create an instance of the real graph class but we prefer to do as late as possible instantiation.
Controlling the appearance of the graph.
Again these methods will be very basic "setter" methods that will just store the user specified options until we need them when we construct the graph
public function SetTitle($aTitle,$aSubTitle) {} public function SetXMinMax($aMin,$aMax) {} public function SetYMinMax($aMin,$aMax) {} public function SetColorIndication($aSpec,$aColors=null) {} public function SetColorMap($aMap) {}
For the color setter we should also add some basic error checking so that the parameter makes sense.
The real worker method
private function Init() {}
Since this is the method is where we will do the actual work and will be fairly large we will spend the entire next section on this method.
The "fake" standard methods. Add() and Stroke(). Since these are fairly short we will show them here in there entirety
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function Add($aPlots) { if( is_array($aPlots) ) { $this->iPlots = array_merge($this->iPlots,$aPlots); } else { $this->iPlots[] = $aPlots; } } function Stroke($aFile='') { $this->Init(); $this->graph->Add($this->iPlots); $this->graph->Stroke($aFile); } |
When adding plots (with the Add()
method) we have to take
into account that the parameter can be either a single object or an
array of plot objects (line and scatter plots) and we must handle that
accordingly as is done above.
For the Stroke()
method we use this to call the
initialization method (to actually create the graph) and then add the
user specified plots and finally call the real
Graph::Stroke()
of the graph.
These methods are very simple but from the outside it will look like a "real" graph class with a clean and simple interface.