📜 ⬆️ ⬇️

“Strategy Pattern. Just a simple "or why I go to the interview" PHP Junior "for fun'a

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 { /** * @param Mixed|stdClass $validatorParam * @return Boolean */ 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: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); // obsolete $param, fixed by domage@habrahabr } 

“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(/*String*/ $email) { return $email === 'Hoho'; // best comparison of ever } } class LengthEmailValidator implements IValidator { public function isValid(/*String*/ $email) { return strlen($email) > 5; } } class LengthMaxEmailValidator implements IValidator { public function isValid(/*String*/ $email) { return strlen($email) <= 100500; } } 

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(/*Collection of IValidators*/ $strategyCollection, /*Any type*/ $param) { foreach($strategyCollection as $strategy) { if($strategy instanceof IValidator) { //       -_- if(!$strategy->isValid($param)) return false; } } return true; } 

Oh yes, this is a pleasant movement of the hair on the head: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:

 //  true,  function IsValidEmailStrong($email = 'habr@habr.ru') { return IsValidByStrategy(array( new HohoEmailValidator(), //  ""  new LengthEmailValidator() //    5  ), $email); } 

The check logic turned out to be shchikarnaya (<- this is not an error):
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 { /** * @param Boolean $validateResult * @param Boolean $validateResultByStep * @return Boolean */ function validateStep(&$validateResult, $validateResultByStep); /** * @return Boolean */ function initialize(); } class ValidReturnRuleAny implements IsValidReturnRule { public function validateStep(&$validateResult, $validateResultByStep) { $validateResult |= $validateResultByStep; return $validateResult; } public function initialize() { return false; } } function IsValidByStrategyByRetRule(/*Collection of IValidators*/ $strategyCollection, /*Any type*/ $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: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.
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.

Source: https://habr.com/ru/post/176505/


All Articles