За последние 24 часа нас посетил 38681 программист и 8133 робота. Сейчас ищут 1722 программиста ...

Полиформизм?

Тема в разделе "PHP для новичков", создана пользователем romt, 10 июл 2023.

  1. romt

    romt Активный пользователь

    С нами с:
    26 ноя 2020
    Сообщения:
    10
    Симпатии:
    0
    Добрый день всем.
    Совершенствуюсь в php.

    Есть объект, у которого некоторое действие должно выполняться по разному в зависимости от некоторых параметров.
    В примере: по разному считается рейтинг для разных категорий людей.
    Вопрос: как это сделать грамотно?

    Поначалу для скорости написал примерно так:
    PHP:
    1. class Person
    2. {
    3.     protected string $category;
    4.    
    5.     protected float $rating;
    6.    
    7.     public function __construct($parameters) {
    8.         // ...
    9.         $this->calculateRating();
    10.         // ...
    11.     }
    12.    
    13.    
    14.     protected function calculateRating() {
    15.        
    16.         switch ($this->category) {
    17.            
    18.             case "FirstCategory":
    19.                 $this->rating = $this->calculateRatingForFirstCategory();
    20.                 break;
    21.             case "SecondCategory":
    22.                 $this->rating = $this->calculateRatingForSecondCategory();
    23.                 break;
    24.             case "ThirdCategory":
    25.                 $this->rating = $this->calculateRatingForThirdCategory();
    26.                 break;
    27.            
    28.         }
    29.        
    30.     }
    31.    
    32.     protected function calculateRatingForFirstCategory() {
    33.         // do something
    34.     }
    35.    
    36.     protected function calculateRatingForSecondCategory() {
    37.         // do something
    38.     }
    39.    
    40.     protected function calculateRatingForThirdCategory() {
    41.         // do something
    42.     }
    43.    
    44. }
    Полагаю, это не есть хороший код.
    В книгах типа "Самоучитель PHP" и "PHP. Разработка веб-приложений" не написано, как правильно.
    В статьях по SOLID я конкретных примеров тоже не увидел. Просто пишется про "расширяемость", но как её реализовать?
    Некоторые статьи вообще советуют не использовать switch и if/elseif/else в таких случаях.

    Пока у меня идея использовать класс-создатель, и в нём устанавливать нужный метод.
    Примерно так:
    PHP:
    1. <?php
    2.  
    3. class Person
    4. {
    5.     protected string $category;
    6.    
    7.     protected float $rating;
    8.    
    9.     //..
    10.     //..
    11.  
    12. }
    13.  
    14.  
    15. class PersonProducer
    16. {
    17.    
    18.     public function getNew($parameters): Person
    19.     {
    20.         $person = new Person();
    21.         //..
    22.         //..
    23.         switch ($parameters->category) {
    24.            
    25.             case "FirstCategory":
    26.                 $calculateRating = $this->calculateRatingForFirstCategory;
    27.                 break;
    28.             case "SecondCategory":
    29.                 $calculateRating = $this->calculateRatingForSecondCategory;
    30.                 break;
    31.             case "ThirdCategory":
    32.                 $calculateRating = $this->calculateRatingForThirdCategory;
    33.                 break;
    34.            
    35.         }
    36.         $person->setCalculateRating = $calculateRating;
    37.         //..
    38.         //..
    39.         return $person;
    40.     }
    41.    
    42.    
    43.    
    44.     protected function calculateRatingForFirstCategory(): float
    45.     {
    46.         // do something
    47.     }
    48.    
    49.     protected function calculateRatingForSecondCategory(): float
    50.     {
    51.         // do something
    52.     }
    53.    
    54.     protected function calculateRatingForThirdCategory(): float
    55.     {
    56.         // do something
    57.     }
    58.    
    59. }
    60.  
    61.  
    62. $personProducer = new PersonProducer();
    63.  
    64. $person = PersonProducer->getNew($parameters);
    Или даже использовать разные классы.
    Примерно так:

    PHP:
    1. class Person
    2. {
    3.     protected string $category;
    4.    
    5.     protected float $rating;
    6.    
    7.     //..
    8.  
    9. }
    10.  
    11. class PersonFirstCategory Extends Person
    12. {
    13.     //..
    14.     protected function calculateRating(): float
    15.     {
    16.         // calculate rating for FirstCategory
    17.     }
    18.     //..
    19. }
    20.  
    21. class PersonSecondCategory Extends Person
    22. {
    23.     //..
    24.     protected function calculateRating(): float
    25.     {
    26.         // calculate rating for SecondCategory
    27.     }
    28.     //..
    29. }
    30. class PersonThirdCategory Extends Person
    31. {
    32.     //..
    33.     protected function calculateRating(): float
    34.     {
    35.         // calculate rating for ThirdCategory
    36.     }
    37.     //..
    38. }
    39.  
    40. class PersonProducer
    41. {
    42.    
    43.     public function getNew($parameters): Person
    44.     {
    45.         switch ($parameters->category) {
    46.            
    47.             case "FirstCategory":
    48.                 $person = new PersonFirstCategory();
    49.                 break;
    50.             case "SecondCategory":
    51.                 $person = new PersonSecondCategory();
    52.                 break;
    53.             case "ThirdCategory":
    54.                 $person = new PersonThirdCategory();
    55.                 break;
    56.            
    57.         }
    58.  
    59.         return $person;
    60.     }
    61.    
    62. }
    63.  
    64.  
    65. $personProducer = new PersonProducer();
    66.  
    67. $person = PersonProducer->getNew($parameters);
    Киньте в меня мануалом с примерами, как писать такие вещи правильно.
    Что бы если через годик мне или другому человеку было меньше проблем изменить/добавить новый расчёт рейтинга.
     
  2. antoniii

    antoniii Активный пользователь

    С нами с:
    16 мар 2022
    Сообщения:
    419
    Симпатии:
    71
    Я бы создал класс Person. Методы и свойства у него определил. Как у тебя в первом случае. Только создал бы всего один метод расчёта рейтинга на все случаи. Но подождем, что скажут другие.
     
    #2 antoniii, 10 июл 2023
    Последнее редактирование: 10 июл 2023
  3. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.178
    Симпатии:
    161
    Адрес:
    GABRIEL
    @romt добрый день.
    Вот хорошая статья на тему полиморфизма.
    И в ней наглядно с примером показано как написать структуру и даже как её можно использовать.

    Хотя и ваши рассуждения по коду о полиморфизме где-то рядом с истиной, но всё равно написаны с ошибками и не реализуют полиморфизм. При этом второй вариант кода совсем что-то мимо.

    Поэтому очень рекомендую вам ознакомиться со статейкой, что я дал выше.
     
    #3 askanim, 10 июл 2023
    Последнее редактирование: 10 июл 2023
  4. Aleksandr.B

    Aleksandr.B Активный пользователь

    С нами с:
    2 фев 2023
    Сообщения:
    159
    Симпатии:
    41
    Адрес:
    Барнаул
    Почитать "PHP 8: объекты, шаблоны и методики программирования, 6-е издание "
    Посмотреть как устроен ArgumentResolver у Symfony.
    Псевдокод:

    PHP:
    1. <?php
    2.  
    3. class Person
    4. {
    5.     protected string $category;
    6.  
    7.     protected RatingInterface $rating;
    8.  
    9.     public function __construct(RatingInterface $rating)
    10.     {
    11.         $this->rating = $rating;
    12.     }
    13.  
    14.     public function calculateRating(): float
    15.     {
    16.         return $this->rating->calculateRating();
    17.     }
    18. }
    19.  
    20. interface RatingInterface
    21. {
    22.     public function calculateRating(): float;
    23.  
    24. }
    25.  
    26. interface RatingForCategoryResolverInterface
    27. {
    28.     public function supports(string $categoryName): bool;
    29.  
    30.     public function resolve(): RatingInterface;
    31. }
    32.  
    33. class FirstCategoryRating implements RatingInterface
    34. {
    35.     public function calculateRating(): float
    36.     {
    37.         return 0;
    38.     }
    39. }
    40.  
    41. class SecondCategoryRating implements RatingInterface
    42. {
    43.     public function calculateRating(): float
    44.     {
    45.         return 1;
    46.     }
    47. }
    48.  
    49. class ThirdCategoryRating implements RatingInterface
    50. {
    51.     public function calculateRating(): float
    52.     {
    53.         return 2;
    54.     }
    55. }
    56.  
    57. class FirstCategoryRatingResolver implements RatingForCategoryResolverInterface
    58. {
    59.  
    60.     public function supports(string $categoryName): bool
    61.     {
    62.         return mb_strtolower("FirstCategory") == mb_strtolower($categoryName);
    63.     }
    64.  
    65.     public function resolve(): RatingInterface
    66.     {
    67.         return new FirstCategoryRating;
    68.     }
    69. }
    70.  
    71. class SecondCategoryRatingResolver implements RatingForCategoryResolverInterface
    72. {
    73.  
    74.     public function supports(string $categoryName): bool
    75.     {
    76.         return mb_strtolower("SecondCategory") == mb_strtolower($categoryName);
    77.     }
    78.  
    79.     public function resolve(): RatingInterface
    80.     {
    81.         return new SecondCategoryRating;
    82.     }
    83. }
    84.  
    85. class ThirdCategoryRatingResolver implements RatingForCategoryResolverInterface
    86. {
    87.  
    88.     public function supports(string $categoryName): bool
    89.     {
    90.         return mb_strtolower("ThirdCategory") == mb_strtolower($categoryName);
    91.     }
    92.  
    93.     public function resolve(): RatingInterface
    94.     {
    95.         return new ThirdCategoryRating;
    96.     }
    97. }
    98.  
    99. class PersonProducer
    100. {
    101.     /**@var RatingForCategoryResolverInterface[] */
    102.     private array $ratings = [];
    103.  
    104.  
    105.     /**
    106.      * @return RatingForCategoryResolverInterface[]
    107.      */
    108.     public function getDefaultRatingResolvers(): array
    109.     {
    110.         return [
    111.             new FirstCategoryRatingResolver,
    112.             new ThirdCategoryRatingResolver,
    113.             new SecondCategoryRatingResolver
    114.         ];
    115.     }
    116.  
    117.     public function addRatingResolver(RatingForCategoryResolverInterface $ratingResolver)
    118.     {
    119.         if (!in_array($ratingResolver, $this->ratings)) {
    120.             $this->ratings[] = $ratingResolver;
    121.         }
    122.     }
    123.  
    124.     public function get($parameters): Person
    125.     {
    126.         $ratings = $this->ratings ?: $this->getDefaultRatingResolvers();
    127.  
    128.         foreach ($ratings as $rating) {
    129.             if ($rating->supports($parameters->category)) {
    130.                 return new Person($rating->resolve());
    131.             }
    132.         }
    133.         throw new RuntimeException(sprintf('Rating by category %s not found', $parameters->category));
    134.     }
    135.  
    136. }
    137.  
    138.  
    139. $personProducer = new PersonProducer();
    140.  
    141. $person = $personProducer->get($parameters);
     
    #4 Aleksandr.B, 11 июл 2023
    Последнее редактирование: 11 июл 2023
    mkramer нравится это.
  5. romt

    romt Активный пользователь

    С нами с:
    26 ноя 2020
    Сообщения:
    10
    Симпатии:
    0
    Спасибо,
    статью почитал,
    книгу скачал, будем изучать.