Разъяснение принципа обращения зависимостей

Извините за кросс-посты по разработке программного обеспечения, не знал, что это не одобряется.

Ответ, который я получил, был именно тем, что я искал, для любопытных: https://softwareengineering.stackexchange.com/a/347143/269571


Оригинальный вопрос

Я читаю книгу «Гибкая разработка программного обеспечения, принципы, шаблоны и практики» от Роберт С. Мартин.

Когда он говорит о Принцип обращения зависимостей он приводит следующий пример нарушения DIP:

Нарушение DIP

Это кажется мне очень ясным, как объект более высокого уровня Button зависит от объекта более низкого уровня Lamp,

Решение, которое он приходит с:

Решение

Он создает интерфейс таким образом, Button больше не зависит от объекта Lamp,

Теория кажется мне действительно ясной, однако я не могу обернуться, используя этот принцип в реальных проектах.

  • Кто собирается определить, какие классы (которые реализуют SwitchableDevice) нужно называться?

  • Кто говорит Button какие устройства ему нужно включать / выключать?

  • Как Вы говорите объект, который использует что-то Аннотация который бетон вещи, которые нужно использовать? (Пожалуйста, поправьте меня, если этот вопрос совершенно не верен)

Если что-то неясно по моему вопросу, пожалуйста, дайте мне знать, я буду рад разъяснить вам кое-что.

3

Решение

Весь смысл внедрения зависимости (по крайней мере, как я понял) заключается в том, что Button не нужно знать, что конкретно SwitchableDevice это переключение.

Абстрактный интерфейс может выглядеть так:

struct SwitchableDevice {
virtual void switchOn() = 0;
virtual void switchOff() = 0;
};

И кнопка может быть реализована так:

struct Button {
SwitchableDevice& dev;
bool state = false;
Button(SwitchableDevice& d) : dev(d) {}
void buttonPress(){
if (state) { dev.switchOff(); }
else       { dev.switchOn();  }
state = !state;
}
};

Для кнопки, вот и все! Никто не должен сказать кнопке, какова конкретная реализация SwitchableDeviceДругими словами: реализация Button и SwitchableDevice развязаны

Возможная реализация Lamp может выглядеть так:

struct Lamp : SwitchableDevice {
void switchOn(){std::cout << "shine bright" << std::endl;}
void switchOff(){std::cout << "i am not afraid of the dark" << std::endl;}
};

И это может быть использовано так:

int main(){
Lamp lamp;
Button button(lamp);
button.buttonPress();
button.buttonPress();
}

Надеюсь, это поможет…

Преимущество состоит в том, что теперь мы можем изменить реализацию Button и Lamp индивидуально, без необходимости что-либо менять с другой стороны. Например, ButtonForManyDevices может выглядеть так:

struct ButtonForManyDevices {
std::vector<SwitchableDevice*> devs;
bool state = false;
Button(std::vector<SwitchableDevice*> d) : devs(d) {}
void buttonPress(){
if (state) for (auto d: devs) { d.switchOff(); }
else       for (auto d: devs) { d.switchOn();  }
state = !state;
}
};

И так же вы можете изменить поведение Lamp полностью (конечно, в пределах SwitchableDevice без необходимости что-либо менять на кнопке. Такой же ButtonForManyDevices может даже использоваться для переключения Lamp, VaccumCleaner и MicroWaveOven,

3

Другие решения

Он говорит, что управление кнопками должно быть более обобщенным, чем просто лампа. Если у вас есть классы кнопок для каждого типа вещей, которыми кнопка может управлять, вы можете получить множество классов кнопок.

В первом примере описывается кнопка на лампе. По сути, он берет лампу за отправную точку и делит ее на составляющие.

Во втором примере он делит части и смотрит на кнопку более широко.

Кто будет определять, какие классы (реализующие SwitchableDevice) нужно вызывать?

Должна быть связь между кнопкой и интерфейсом.

Кто скажет Баттону, какие устройства ему нужно включать / выключать?

Класс Button должен был бы реализовать механизм, позволяющий определить, к какому устройству он подключен.

Как вы говорите объекту, который использует что-то абстрактное, какие конкретные вещи ему нужно использовать? (Пожалуйста, поправьте меня, если этот вопрос совершенно не верен).

Потому что объект, производный от абстрактного интерфейса, должен полностью реализовывать интерфейс. Объект Lamp должен где-то определять методы TurnOn и TurnOff.

0

По вопросам рекламы [email protected]