#oop #design-patterns
#ооп #шаблоны проектирования
Вопрос:
Этот вопрос не зависит от языка для языков ООП.
У меня есть два класса: DeviceA
и DeviceB
, которые наследуются от класса модели Device
. Мне нужно реализовать две службы DeviceAService
и DeviceBService
, которые наследуются от DeviceService
. Мой язык не поддерживает обобщения, и мне нужно реализовать методы с Device
параметром в DeviceService
, которые будут переопределены в DeviceAService
и DeviceBService
и реализованы по-разному в зависимости от типа Device
. Затем я хочу вызвать эти функции из общего DeviceService
.
Какова наилучшая практика для разработки этого? Нельзя if
ли здесь избежать использования и проверки типа класса? Еще одна идея для оптимальной реализации этого дизайна?
Комментарии:
1. Это звучит как процедурное программирование, а не ООП. Данные находятся в одном классе; логика — в другом. Вы должны передать структуру данных в процедуру. ООП объединит их и реализует логику в одном классе с необходимыми данными. Тогда логика становится полиморфной.
2. У вас могут быть классы DeviceA и DeviceB, наследуемые от устройства, а также от службы смешивания, или устройство наследуется от (или используется) службы, если нет множественного наследования (и, возможно, тогда смешивание называется интерфейсом)
3. Также возможно, чтобы объекты содержали специализированные объекты-службы.
Ответ №1:
Если я правильно понимаю вопрос, у вас есть абстрактный базовый класс, подобный этому:
public abstract class DeviceService
{
public abstract void DoSomething(Device device);
}
Вы хотите реализовать DeviceAService
и DeviceBService
так, чтобы их поведение отличалось в зависимости от типа Device
.
Полиморфизм
Наиболее объектно-ориентированный дизайн заключается в использовании полиморфизма. Поскольку Device
он уже полиморфен, поместите поведение, которое вы хотите изменить, в полиморфный метод и вызовите его из своего сервиса:
public override void DoSomething(Device device)
{
// Perhaps do something first...
device.DoThePolymorphicThing();
// Perhaps do something else after...
}
Реализуйте DoThePolymorphicThing
по-разному в DeviceA
и DeviceB
.
Открытый мир
Если вы не можете изменить API Device
базового класса, и вам также необходимо поддерживать другие реализации, кроме DeviceA
and DeviceB
, вам, вероятно, потребуется выполнить проверку во время выполнения DoSomething
:
public override void DoSomething(Device device)
{
if (device is DeviceA)
// Do something...
else if (device is DeviceB)
// Do something else...
else
// remember to handle the case where it's neither A nor B
}
Мы можем назвать этот тип сценария «открытым миром», потому Device
что он открыт для расширения.
Закрытый мир
Если, с другой стороны, вы знаете, что у вас будет только DeviceA
и DeviceB
, шаблон проектирования посетителя — это именно то, что вам нужно:
public interface IDeviceVisitor
{
void VisitA(DeviceA device);
void VisitB(DeviceB device);
}
public abstract class Device
{
public abstract void Accept(IDeviceVisitor visitor);
// Other members go here if you need them...
}
public class DeviceA : Device
{
public override void Accept(IDeviceVisitor visitor)
{
visitor.VisitA(this);
}
// Other overrides, if needed, go here...
}
// Define DeviceB in the same way...
Теперь вы можете реализовать сервис следующим образом:
public override void DoSomething(Device device)
{
// Perhaps do something first...
device.Accept(new MyDeviceVisitor());
// Perhaps do something else after...
}
Вы можете думать об этом сценарии как о сценарии «закрытого мира», потому что вы не можете добавить третий тип устройства без изменения API посетителя (и, следовательно, нарушения существующих реализаций).
Комментарии:
1. Я нахожу ваш подход очень интересным. Однако я бы предпочел сохранить свои классы устройств в виде простых моделей и не внедрять в них какие-либо методы бизнес-логики. Я бы предпочел реализовать свои методы в сервисах. Тем не менее, ваш вклад очень полезен и является жизнеспособным подходом.