Greeting
Hello! On this joyful and, sufficiently, warm Friday day, I happened to (I quote in a more pleasant version) “hand-face”. To be honest, this action happens quite often, but, as usual, it is caused by the sensation:
God, how stupid I am.
This time I had a slightly different feeling, and, as I was convinced, not only me. This feeling reminded me of one of my interviews, where I was asked to write the skeleton of the “Decorator” pattern, which turned out to be completely different from the interviewer's personal view than his classical interpretation.
In order not to disappoint and introduce the reader’s brain to the “cognitive dissonance”, I’ll clarify that this article deals with a “hypothetical” interview in this
company , so if I were asked to tell about the “Strategy” pattern. As in the previous case, my answer would have been at least doubtful in the eyes of the interviewer.
Do not pry
Knowing that in the electronic storehouse of knowledge there is a 100% article on the Strategy Design Pattern, with an example in C ++, which is most likely taken from a GoF book, and in all known programming languages (and suddenly, including esoteric), my task was: without peeping shove a similar example from an
article into a pattern concept. Let's see what needs to be done and what happened.
')
Saw, shura, saw
In my opinion, it is not good to operate with specific types of classes: in this case we have not only knowledge about
the class
interface , but also about the
implementation , including usually a bunch of additional methods (often open ===
public ). Instead, an
interface- based approach is proposed, so the first step of refactoring an example would be to highlight it:
interface IValidator { function isValid($validatorParam); }
I allowed myself to relax a bit and therefore the code will contain the minimum number of comments (in fact, the code contains the minimum number of comments in my IDE). Total: an entity that is capable of issuing a validity verdict on the argument passed. It is worth writing that in this place, like you, I begin to distort:
- The number of arguments is strictly fixed (the variable array is not our method)
- The problem above cannot be solved by overloading the function (yes, C ++ looks at PHP as on ...)
- Argument type is not defined (although for many PHP programmers this is not a problem)
But I exhale (just air), accept limitations and continue to write. Based on the name of the pattern, we should have some mechanism (or
opportunity ) to apply different
strategies in different cases (or use cases), for example, depending on external factors, and not only at the compilation stage, but also ... oh, not the right language. In a formalized way, our desire will look something like this:
function Validate(IValidator $validateStrategy, $param) { return $validateStrategy->isValid($param); }
And the
adaptation of our desire for validation, for example, the username will look like this:
function ValidateName($userName) { return Validate(new NameValidator(), $userName);
“I agree,” I will answer you, attentive reader, to your invisible question:
Is the constructor called every time? Can a singleton or static function variable, not?
But these are questions of the next refactoring. Let's go back to the subject area of the example: we need entities for validation, for example, a mailbox (email), and answering questions: is this Hoho address? Is this address not too short or long?
class HohoEmailValidator implements IValidator { public function isValid( $email) { return $email === 'Hoho';
So, as our
Validate () function does not allow us to use a collection (list, array) of validators, we will have to expand its functionality using the collection (list, array) of validators as an argument:
function IsValidByStrategy( $strategyCollection, $param) { foreach($strategyCollection as $strategy) { if($strategy instanceof IValidator) {
Oh yes, this is a pleasant movement of the hair on the head:
- The state when the validator collection is empty is not tracked.
- You cannot change the behavior of a function: it falls out when the first isValid () function is false
Now that we have all the cards in hand, we can describe the first, less useful function in the context of the pattern
strategy , which checks the postal address given to it:
The check logic turned out to be shchikarnaya (<- this is not an error):
- Email should be the string 'Hoho' - which is already crazy
- Well, even if the address is really 'Hoho', it will never be more than 5 characters.
- But it should be noted that checking with LengthEmailValidator in this context will never be performed.
Addition
To make the example less idiotic (remember the
LengthEmailValidator ), we can apply the
strategy pattern once more, but with reference to changing the behavior of the
IsValidByStrategy () function and implement the following functionality:
interface IsValidReturnRule { function validateStep(&$validateResult, $validateResultByStep); function initialize(); } class ValidReturnRuleAny implements IsValidReturnRule { public function validateStep(&$validateResult, $validateResultByStep) { $validateResult |= $validateResultByStep; return $validateResult; } public function initialize() { return false; } } function IsValidByStrategyByRetRule( $strategyCollection, $param, IsValidReturnRule $retStrategy) { $result = $retStrategy->initialize(); foreach($strategyCollection as $strategy) { if($strategy instanceof IValidator) { if($retStrategy->validateStep($result, $strategy->isValid($param))) return $result; } } return $result; }
Total we received:
- Ability to change behavior during validation
- The ability to change the behavior of the validation process
Now, in order to verify that any email address will be considered valid, we will need to write the following code:
class ValidReturnRuleAny implements IsValidReturnRule { public function validateStep(&$validateResult, $validateResultByStep) { $validateResult |= $validateResultByStep; return $validateResult; } public function initialize() { return false; } } function IsValidEmailSoft($email = 'habr@habr.ru') { return IsValidByStrategyByRetRule(array( new HohoEmailValidator(), new LengthEmailValidator() ), $email, new ValidReturnRuleAny()); }
Due to the use of a
strategy for processing the result of
a validation
strategy , the
IsValidEmailSoft function will not object to any addresses that are no longer than 100,500 characters, i.e.
- IsValidEmailStrong ()
- The address is invalid if any of the validation strategies returned false
- IsValidEmailSoft ()
- The address is valid if any of the validation strategies returned true
What you need to remember
Any design solution that implements an elegant solution (including language features) of a general task has limitations of use. In this sense, the book
Design Patterns by GoF has been beautifully designed.
Conclusion
I'm afraid this article will be an example, as it would seem, simple things, you can "pour so much water." But I am sure of one thing: it would be great if, after this article, those of us who for some reason neglected or were afraid to get acquainted with the design
patterns win their fear.
In the end: knowledge is better than ignorance, and light is better than darkness. Thank you and have a great weekend!
Upd:PS Yes, this is my first article, so: congratulations, criticism, insults, mistakes \ ochepyatki accepted in PM.