If your application is well thought out, you would not want to save any data that isn’t valid. So what do you do, when you need different validation schemes, say for passwords, depending on some special case?
For example: Your user passwords need to be at least 8 characters long and contain upper and lower case letters, a number and a special character, but when one of your users forget their password, you would like to generate a new one which does not have to conform to those conditions.
There are some approaches to this, such as using more than one validation function in your user object, or using some boolean flag to enable or disable the validation. These are kind of clunky, though, and there is a better approach: Using a “policy” – Policies can be used for other things than this too, but let’s look at how to use a policy for managing password validation.
An example scenario
First, let’s look at the example scenario of user passwords a bit more closely.
We have a User object, which contains code that will validate a newly set password. The code that’s responsible for saving users to the DB will not allow us to store anything that does not validate, because having invalid data stored is not a very good idea.
The user works something like this:
//set a valid password $user->setPassword('Xyz123!'); //true $user->isValid(); //set an invalid password $user->setPassword('zxcqwe'); //no longer true $user->isValid(); |
In the case where we have a simpler, invalid, password it could be generated by some algorithm to create random simple passwords for users when they forget their own.
As mentioned before, we could add some flag to isValid or have a separate method for setting the random password which makes it ignore the validation. While these are easy to implement, they aren’t very obvious: A boolean parameter to isValid could mean a lot more than “ignore password validation”, and using two methods has similar problems. It would also make it a bit more difficult to modify our password validation rules later.
Instead, we’ll create some policy objects and use those.
What is a policy?
In our example, a policy means the rules used to validate a password. We could call it a password validator too, but I think a policy is a quite descriptive word for this, as we only want to set a specific policy like “RandomPasswordPolicy” – we don’t really care how it validates the password, as long as randomly generated passwords validate with it. In fact, it might even just not validate it at all and always return that it’s valid.
The policy object will define a method to call to check if a password validates with the policy. This way we can define a policy, assign it to the user, and then the password will get validated based on the policy.
By default, we can have a policy like this:
//Let's just assume the IPasswordPolicy interface defines the validatePassword method class ComplexPasswordPolicy implements IPasswordPolicy { public function validatePassword($password) { //password must be at least 8 chars long if(strlen($password) < 8) { return false; } //password must contain an upper case letter if(preg_match('/[A-Z]/', $password) == 0) { return false; } //password must contain a number if(preg_match('/[0-9]/', $password) == 0) { return false; } //and a special character if(preg_match('/[^A-Za-z0-9]/', $password) == 0) { return false; } return true; } } |
For the randomly generated password, we’ll have this very simple policy:
class RandomPasswordPolicy implements IPasswordPolicy { public function validatePassword($password) { return true; } } |
Using the policy with the user
Now that we have a policy with the validation rules, we will need to change the user’s isValid method to test the password using the policy.
By default, the user object will come with the ComplexPasswordPolicy, so any new user that is created will need a good password and if a user wishes to change their password, it will also need to pass the complex validation.
However, in our code which generates a new password for users who forget theirs, we can now easily swap the validation policy to the simple one:
//We'll assume $user is the user who forgot their password $user->setPasswordPolicy( new RandomPasswordPolicy() ); $user->setPassword($randomPassword); //Now the user will validate even if the randomly generated password isn't very complex $user->isValid(); |
Conclusion
Using a swappable policy is useful if you need to be able to change some aspect of your code. This is quite similar to the strategy pattern too, it just has a different name.