Answer the question
In order to leave comments, you need to log in
How to draw a correspondence between a string and a class in terms of SOLID?
Здравствуйте. Подскажите как правильно соотнести например, получаемую Строку извне строку и Класс
и при этом выполнить Принцип открытости/закрытости из SOLID?
Например, извне я получаю строку $animal_type="cat" и должен вызвать метод say() класса Cat;
Первый вариант: через оператор switch
class Human {
public function touchAnimal($animal_type)
{
switch ($animal_type) {
case 'dog':
$animal = new Dog();
break;
case 'cat':
$animal = new Cat();
break;
}
$animal->say();
}
}
class Human {
$home_animals = [
'dog' => Dog::class,
'cat' => Cat::class,
];
public function touchAnimal($animal_type)
{
$animal = new $this->home_animals[$animal_type]();
$animal->say(); // IDE не знает объектом какого класса является переменная и не подсказывает
}
}
class Human {
public function touchAnimal($animal: VoiceAnimal) // или интерфейс IVoiceAnimal
{
$animal->say();
}
}
class VoiceAnimal
{
public $sound = '';
public function say()
{
print_r('Animal says: ' . $this->sound);
}
}
class Dog extends VoiceAnimal
{
public $sound = 'GAV-GAV';
public function say()
{
print_r('DOG SAY: ' . $this->sound);
}
}
class Cat extends VoiceAnimal
{
public $sound = 'MYAU';
public function say()
{
print_r('CAT SAY: ' . $this->sound);
}
}
Answer the question
In order to leave comments, you need to log in
Это всё очень плохо.
В первом варианте человек должен знать, как мяукают кошки, а в последнем "правильном" варианте человек трогает не кошку, а кошачий голос(?!).
В "идеальном" варианте опять же выбирается не животное, которое надо погладить, а его голос.
Чтобы следовать принципам солид, надо понять в первую очередь ЗАЧЕМ это всё делается.
А делается это для того чтобы уменьшить связность. Чтобы класс, использующий какой-либо функционал, не знал деталей его реализации. И, соответственно, мы могли бы менять реализацию без опасения поломать что-то в классе-пользователе.
При этом extends, кроме как от абстрактного класса, эту связность всегда увеличивает.
И его надо избегать. А использовать принцип Composition over inheritance. То есть нужный функционал получать не наследованием, а передачей независимых функциональных модулей в виде параметров.
Соответственно, нам надо сделать иерархию: голос - животное - потрогать.
И вот теперь у нас хоть голос, хоть животное, будут открыты для каких угодно изменений, до тех пор пока они поддерживают публичный контракт.
/ ******* голоса *******/
abstract class VoiceEngine {
public function getVoice() {}
}
class CatVoiceEngine extends VoiceEngine {
public function getVoice() {
return "Meow!";
}
}
class DogVoiceEngine extends VoiceEngine {
public function getVoice() {
return "Bark!";
}
}
class HumanVoiceEngine extends VoiceEngine {
public function getVoice() {
return "Да пошёл ты!";
}
}
/ ******* животные *******/
abstract class Animal {
public function __construct(public VoiceEngine $voiceEngine) {}
public function say() {
echo $this->voiceEngine->getVoice();
}
}
class Cat extends Animal{}
class Dog extends Animal{}
class Human extends Animal{
public function touchAnimal(Animal $animal) {
$animal->say();
}
}
/ ******* исполнение *******/
$cat = new Cat(new CatVoiceEngine());
$dog = new Dog(new DogVoiceEngine());
$human = new Human(new HumanVoiceEngine());
$human->touchAnimal($cat);
$human->touchAnimal($dog);
$human->touchAnimal($human);
class AnimalFactory {
public static function create($type) {
return match($type) {
'cat' => new Cat(new CatVoiceEngine()),
'dog' => new Dog(new DogVoiceEngine()),
'human' => new Human(new HumanVoiceEngine()),
};
}
}
$human = new Human(new HumanVoiceEngine());
$human->touchAnimal(AnimalFactory::create('cat'));
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question