#c# #oop #enums #polymorphism #model-associations
#c# #ооп #перечисления #полиморфизм #модель-ассоциации
Вопрос:
Я использую an enum
для обозначения двух разных классов продуктов — SensorDevice и [data] CaptureDevice . Они предназначены для использования физически подключенными друг к другу, но не каждая модель датчика может использоваться каждой моделью приемника, поэтому я создал перечисление для разных платформ, поэтому графический интерфейс будет отображать только совместимые аксессуары для данного устройства:
enum Platform {
Standard,
Deluxe,
Jumbo
}
abstract class CaptureDevice
{
public Platform platform;
public bool IsCompatibleWith(SensorDevice sd)
{
return sd.Platforms.Contains(this.Platform); // <- this is type-checking!
}
}
abstract class SensorDevice
{
public IEnumerable<Platform> Platforms;
public bool IsCompatibleWith(CaptureDevice cd)
{
return this.Platforms.Contains(cd.Platform); // <- this is type-checking!
}
}
Я начал находить это довольно неприятным, поскольку перечисление жестко запрограммировано. Я рассматриваю возможность использования рефакторинга «Заменить код типа полиморфизмом», но я не совсем уверен, как это сделать в данной ситуации. Есть предложения?
Комментарии:
1. Как вы выбираете, что
Platform
для чегоDevice
? Это по имени правила или полностью произвольно? Можете ли вы показать некоторые реализованные устройства?2. Пусть platfom спросит датчик, совместим ли он. Это позволит вам скрыть перечисление платформ на датчике, а затем при необходимости реорганизовать датчики, чтобы исключить использование перечисления платформы там. Однако это может быть больше работы, поскольку есть польза.
3. @romain-aga правило является произвольным. Теоретически это связано с геометрическими и электрическими характеристиками датчика, но также с коммерческими соображениями и соображениями портфолио. Так что да, это произвольно. Кроме того, нет никакой уверенности в том, что в будущем другие модели платформы будут недоступны, с нежелательной необходимостью «перекомпилировать» перечисление.
4. @ReneA Если я правильно понимаю, в настоящее время это происходит при вызове
aCaptureDevice.IsCompatibleWith(aSensorDevice)
. То, что я ищу, — это способ реализовать эту подпись без использованияenum
вообще!
Ответ №1:
Вы могли бы сделать следующее, чтобы избавиться от enum .
Замените перечисление классом.
public abstract class Platform {}
Добавьте Device
класс, который отвечает, совместим ли он с a Platform
.
public abstract class Device
{
public abstract bool IsCompatibleWith(Platform platform);
}
Создайте CaptureDevice
подкласс Device
.
public abstract class CaptureDevice : Device
{
public Platform Platform;
public override bool IsCompatibleWith(Platform platform)
{
// I'm using type comparison for the sake of simplicity,
// but you can implement different business rules in here.
return this.Platform.GetType() == platform.GetType();
}
public bool IsCompatibleWith(SensorDevice sd)
{
// We are compatible if sensor is compatible with my platform.
return sd.IsCompatibleWith(this.Platform);
}
}
Создайте SensorDevice
подкласс Device
.
public abstract class SensorDevice : Device
{
public IEnumerable<Platform> Platforms;
public override bool IsCompatibleWith(Platform platform)
{
// I'm using type comparison again.
return this.Platforms.Any(p => p.GetType() == platform.GetType());
}
public bool IsCompatibleWith(CaptureDevice cd)
{
// We are compatible if capture is compatible with one of my platforms.
return this.Platforms.Any(p => cd.IsCompatibleWith(p));
}
}
В принципе, это все, что вам нужно сделать. Чтобы проиллюстрировать, как это работает, я добавил приведенный ниже код.
// Platforms
public class StandardPlatform : Platform { }
public class DeluxPlatform : Platform { }
public class JumboPlatform : Platform { }
// Capture device(s)
public class CD1 : CaptureDevice
{
public CD1()
{
Platform = new StandardPlatform();
}
}
// Sensor device(s)
public class SD1 : SensorDevice
{
public SD1()
{
Platforms = new List<Platform>
{
new StandardPlatform(),
new DeluxPlatform()
};
}
}
public class SD2 : SensorDevice
{
public SD2()
{
Platforms = new List<Platform> {new JumboPlatform()};
}
}
// Somewhere in the code...
var cd1 = new CD1();
var sd1 = new SD1();
Console.WriteLine(cd1.IsCompatibleWith(sd1)); // True
Console.WriteLine(sd1.IsCompatibleWith(cd1)); // True
var sd2 = new SD2();
Console.WriteLine(sd2.IsCompatibleWith(cd1)); // False
Console.WriteLine(cd1.IsCompatibleWith(sd2)); // False
Ответ №2:
РЕДАКТИРОВАТЬ: если вы хотите избавиться от перечисления, почему бы не использовать файл конфигурации?
Вы настраиваете свои платформы следующим образом:
<Platforms>
<Platform name="Standard">
<Device>Namespace.of.your.Device.CaptureDevice</Device>
<Device>Namespace.of.another.Device.AnotherDevice</Device>
</Platform>
<Platform name="Deluxe">
<Device>Namespace.of.your.Device.CaptureDevice</Device>
</Platform>
</Platforms>
Другой синтаксис:
<Platforms>
<Platform>
<Name>Standard</Name>
<Devices>
<Device>Namespace.of.your.Device.CaptureDevice</Device>
<Device>Namespace.of.another.Device.AnotherDevice</Device>
<Devices>
</Platform>
</Platforms>
Тогда у вас будет та же базовая структура, вы могли бы сделать что-то вроде этого:
abstract class ADevice<T> where T : ADevice<T>
{
public ADevice<T>(T device , string filename)
{
// Parsing of the file
// Plus, setting the Platforms Property
}
// Or (but you should keep the first constructor to see the filename dependency)
public ADevice<T>(T device)
{
// Parsing of the file
}
public IEnumerable<Platform> Platforms { get; private set; }
public bool IsCompatibleWith(T device)
{
return this.Platforms.Contains(device.Platform); // <- this is type-checking!
}
}
Затем:
abstract class CaptureDevice : ADevice<CaptureDevice>
{
public CaptureDevice(CaptureDevice device, string filename)
: base(device, filename)
{
}
// Or (But still same comment)
public CaptureDevice(CaptureDevice device)
: base(device, defaultFilename)
{
}
}
То же самое для SensorDevice
И вместо Enum
, вы можете создать простой класс на данный момент как:
public class Platform
{
public Platform(string name)
{
Name = name;
}
public string Name { get; }
}
Комментарии:
1. @heltonbiker, я отредактировал свой пост, если вы все еще ищете решение по поводу этого перечисления. Это может вас заинтересовать.