Before I start demonstrating how to implement the Strategy design pattern for checking incoming data in PHP, first it would be quite educational to set up an example that performs the same validation process through a single, pretty monolithic class. Once you see the drawbacks of using an approach like this more clearly, it’ll be easier for you to understand how the Strategy pattern can tackle the process more efficiently. Having clarified that point, here’s the validation class that I plan to use at this initial stage: <?php class Validator {
protected $_errors = array();
// constructor (not implemented) public function __construct(){}
// validate integers public function checkInteger($value) { if (!is_int($value)) { $this->_errors[strtolower($value)] = 'The supplied value must be an integer.'; } return $this; }
// validate floats public function checkFloat($value) { if (!is_float($value)) { $this->_errors[strtolower($value)] = 'The supplied value must be a float number.'; } return $this; }
// validate strings public function checkString($value) { if (!is_string($value)) { $this->_errors[strtolower($value)] = 'The supplied value must be a string.'; } return $this; }
// get validation errors public function getErrors() { return $this->_errors; }
// clear validation errors public function clearErrors() { $this->_errors = array(); }
// validate supplied data public function validate() { return empty($this->_errors) ? true : false; } } As you can see above, the example “Validator” class implements a few simple methods which are mere proxies for the native “is_int(),” “is_float()” and “is_string()” PHP functions. Besides that, the class defines an additional “validate()” method for determining whether or not the supplied data is valid. So far, there’s nothing unexpected with the inner workings of this class, right? Having already explained how the previous “Validator” does its business, it’s time to give it a try and test its real functionality. The script below uses the class’s main methods for checking the validity of some trivial data. Take a look at it, please: $validator = new Validator; // validate some basic data $validator->checkInteger(1234) ->checkFloat('abcde') ->checkString(12.345); if (!$validator->validate()) { // display validation errors $errors = $validator->getErrors(); foreach ($errors as $value => $error) { echo 'The value ' . $value . ' is incorrect. ' . $error . '<br />'; } /* displays the following The value abcde is incorrect. The supplied value must be a float number. The value 12.345 is incorrect. The supplied value must be a string. */ } else { echo 'Congratulations! Your data is correct!'; } Well, that worked decently well. Once an instance of the “Validator” class has been spawned, checking for integers, floats and strings is only a matter of chaining the corresponding validation methods and displaying the errors that might have occurred during the verification process. It’s that simple, really. At this point, you may be wondering what could be wrong with the “Validator” class if it yields such good results (at least for example purposes). First off, the model is not very flexible; the only way to extend the class’s functionality is via Inheritance. And last, but not least, I have to say that “Validator” looks somewhat like a god class because it attempts to validate every type of data through a set of discrete methods. It becomes evident that a class like this should be used as a helper only in simple use cases. However, as an application grows in complexity, it’s necessary to implement a more modular, powerful and flexible solution. And here’s exactly where the Strategy pattern comes into play, since it permits you to easily apply the two “great commandments” of the object-oriented paradigm mentioned in the introduction. In simple terms, the pattern uses Composition to inject an instance of a class that encapsulates certain functionality into another one, which allows you to perform a task according to a predefined strategy. If this explanation sounds somewhat confusing to you, let me elaborate this concept a bit further with the example that you just saw. Instead of defining a class that tries to validate everything via a bunch of concrete methods, it’d be more effective to split it up in a group of smaller classes, where each one would be responsible for checking only one type of data. This would encapsulate the concept that varies, in this case the validation process itself. Finally, each of these validation classes would be injected into a helper class, which would make it possible to check incoming data according to a certain validation strategy (i.e. an integer validation strategy, a float validation strategy and so forth). Got the point? I hope so. Of course, by far the best way to understand how the Strategy pattern can be used to validate input data is by showing some functional code. In keeping with this, in the next section I’m going to define the bare bones structure of a whole new helper class, which will accept via Composition the set of discrete validation classes mentioned before. To see how this helper class will be created, read the lines to come.
blog comments powered by Disqus |
|
|
|
|
|
|
|