Добрый день всем. Совершенствуюсь в php. Есть объект, у которого некоторое действие должно выполняться по разному в зависимости от некоторых параметров. В примере: по разному считается рейтинг для разных категорий людей. Вопрос: как это сделать грамотно? Поначалу для скорости написал примерно так: PHP: class Person { protected string $category; protected float $rating; public function __construct($parameters) { // ... $this->calculateRating(); // ... } protected function calculateRating() { switch ($this->category) { case "FirstCategory": $this->rating = $this->calculateRatingForFirstCategory(); break; case "SecondCategory": $this->rating = $this->calculateRatingForSecondCategory(); break; case "ThirdCategory": $this->rating = $this->calculateRatingForThirdCategory(); break; } } protected function calculateRatingForFirstCategory() { // do something } protected function calculateRatingForSecondCategory() { // do something } protected function calculateRatingForThirdCategory() { // do something } } Полагаю, это не есть хороший код. В книгах типа "Самоучитель PHP" и "PHP. Разработка веб-приложений" не написано, как правильно. В статьях по SOLID я конкретных примеров тоже не увидел. Просто пишется про "расширяемость", но как её реализовать? Некоторые статьи вообще советуют не использовать switch и if/elseif/else в таких случаях. Пока у меня идея использовать класс-создатель, и в нём устанавливать нужный метод. Примерно так: PHP: <?php class Person { protected string $category; protected float $rating; //.. //.. } class PersonProducer { public function getNew($parameters): Person { $person = new Person(); //.. //.. switch ($parameters->category) { case "FirstCategory": $calculateRating = $this->calculateRatingForFirstCategory; break; case "SecondCategory": $calculateRating = $this->calculateRatingForSecondCategory; break; case "ThirdCategory": $calculateRating = $this->calculateRatingForThirdCategory; break; } $person->setCalculateRating = $calculateRating; //.. //.. return $person; } protected function calculateRatingForFirstCategory(): float { // do something } protected function calculateRatingForSecondCategory(): float { // do something } protected function calculateRatingForThirdCategory(): float { // do something } } $personProducer = new PersonProducer(); $person = PersonProducer->getNew($parameters); Или даже использовать разные классы. Примерно так: PHP: class Person { protected string $category; protected float $rating; //.. } class PersonFirstCategory Extends Person { //.. protected function calculateRating(): float { // calculate rating for FirstCategory } //.. } class PersonSecondCategory Extends Person { //.. protected function calculateRating(): float { // calculate rating for SecondCategory } //.. } class PersonThirdCategory Extends Person { //.. protected function calculateRating(): float { // calculate rating for ThirdCategory } //.. } class PersonProducer { public function getNew($parameters): Person { switch ($parameters->category) { case "FirstCategory": $person = new PersonFirstCategory(); break; case "SecondCategory": $person = new PersonSecondCategory(); break; case "ThirdCategory": $person = new PersonThirdCategory(); break; } return $person; } } $personProducer = new PersonProducer(); $person = PersonProducer->getNew($parameters); Киньте в меня мануалом с примерами, как писать такие вещи правильно. Что бы если через годик мне или другому человеку было меньше проблем изменить/добавить новый расчёт рейтинга.
Я бы создал класс Person. Методы и свойства у него определил. Как у тебя в первом случае. Только создал бы всего один метод расчёта рейтинга на все случаи. Но подождем, что скажут другие.
@romt добрый день. Вот хорошая статья на тему полиморфизма. И в ней наглядно с примером показано как написать структуру и даже как её можно использовать. Хотя и ваши рассуждения по коду о полиморфизме где-то рядом с истиной, но всё равно написаны с ошибками и не реализуют полиморфизм. При этом второй вариант кода совсем что-то мимо. Поэтому очень рекомендую вам ознакомиться со статейкой, что я дал выше.
Почитать "PHP 8: объекты, шаблоны и методики программирования, 6-е издание " Посмотреть как устроен ArgumentResolver у Symfony. Псевдокод: PHP: <?php class Person { protected string $category; protected RatingInterface $rating; public function __construct(RatingInterface $rating) { $this->rating = $rating; } public function calculateRating(): float { return $this->rating->calculateRating(); } } interface RatingInterface { public function calculateRating(): float; } interface RatingForCategoryResolverInterface { public function supports(string $categoryName): bool; public function resolve(): RatingInterface; } class FirstCategoryRating implements RatingInterface { public function calculateRating(): float { return 0; } } class SecondCategoryRating implements RatingInterface { public function calculateRating(): float { return 1; } } class ThirdCategoryRating implements RatingInterface { public function calculateRating(): float { return 2; } } class FirstCategoryRatingResolver implements RatingForCategoryResolverInterface { public function supports(string $categoryName): bool { return mb_strtolower("FirstCategory") == mb_strtolower($categoryName); } public function resolve(): RatingInterface { return new FirstCategoryRating; } } class SecondCategoryRatingResolver implements RatingForCategoryResolverInterface { public function supports(string $categoryName): bool { return mb_strtolower("SecondCategory") == mb_strtolower($categoryName); } public function resolve(): RatingInterface { return new SecondCategoryRating; } } class ThirdCategoryRatingResolver implements RatingForCategoryResolverInterface { public function supports(string $categoryName): bool { return mb_strtolower("ThirdCategory") == mb_strtolower($categoryName); } public function resolve(): RatingInterface { return new ThirdCategoryRating; } } class PersonProducer { /**@var RatingForCategoryResolverInterface[] */ private array $ratings = []; /** * @return RatingForCategoryResolverInterface[] */ public function getDefaultRatingResolvers(): array { return [ new FirstCategoryRatingResolver, new ThirdCategoryRatingResolver, new SecondCategoryRatingResolver ]; } public function addRatingResolver(RatingForCategoryResolverInterface $ratingResolver) { if (!in_array($ratingResolver, $this->ratings)) { $this->ratings[] = $ratingResolver; } } public function get($parameters): Person { $ratings = $this->ratings ?: $this->getDefaultRatingResolvers(); foreach ($ratings as $rating) { if ($rating->supports($parameters->category)) { return new Person($rating->resolve()); } } throw new RuntimeException(sprintf('Rating by category %s not found', $parameters->category)); } } $personProducer = new PersonProducer(); $person = $personProducer->get($parameters);