Я разрабатываю систему, которая работает с несколькими источниками данных. Эти источники данных имеют идентификаторы, которые могут выполнять определенные проверки при их создании. Это позволяет получать исторические данные и данные в реальном времени. Моя текущая абстракция включает в себя три базовых класса.Сильно связанные классы и типы безопасных абстракций
Класс DataSource отвечает за подключения к службам и поддерживает эзотерические вещи для этого источника данных, включая способы открытия и закрытия соединений и проблемы безопасности потоков в каждом конкретном случае.
Класс DataContext отвечает за то, какие значения должны быть получены, будь то в реальном времени или в историческом, а также в какой дате и т. Д. Могут быть и другие контексты, где вы можете пожелать смешанных исторических дат (параметризованных), среди прочего, поэтому я хочу используйте полиморфизм для достижения этого.
Класс Identifier отвечает за синтаксический анализ строк и проверку правильности выражений, чтобы убедиться, что строковый идентификатор, который передается, в некоторой степени действителен. Он также используется для безопасности типа, поскольку идентификатор для одного источника данных не может быть передан другому источнику данных.
См. Пример кода ниже;
public class DataSource
{
// base class for all data sources
// maintains connections opening and closing plus
// thread safety concerns
}
public class FooDataSource : DataSource { }
public class BarDataSource : DataSource { }
public abstract class Identifier
{
public string Value { get; internal set; }
public Identifier(string value)
{
Value = value;
}
}
public class FooIdentifier : Identifier
{
public FooIdentifier(string value) : base(value)
{
// checks go here on the string that comes in
// specific to the foo data source
}
}
public class BarIdentifier : Identifier
{
public BarIdentifier(string value) : base(value)
{
// checks on the string values that come in for the Bar
// source
}
}
public abstract class DataContext<TIdentifier> where TIdentifier : Identifier
{
public abstract double GetValue(TIdentifier id);
}
public abstract class FooDataContext : DataContext<FooIdentifier> { }
public abstract class BarDataContext : DataContext<BarIdentifier> { }
public class FooRealTimeDataContext : FooDataContext
{
public override double GetValue(FooIdentifier id)
{
// real implementation here
return -1;
}
}
public class BarRealTimeDataContext : BarDataContext
{
public override double GetValue(BarIdentifier id)
{
// real implementation here
return -10;
}
}
[TestFixture]
public static class TestMe
{
[Test]
public static void MyTest()
{
// create the data context (to get data from)
var ctx = new FooRealTimeDataContext();
ctx.GetValue(new FooIdentifier("onetuhoenthuo")); // compiles (good)
// ctx.GetValue(new BarIdentifier("noetuhneoth")); // does not compile (also good)
}
}
вопрос (наконец) как я могу создать класс, который на самом деле следует ООП принципалов для заполнения следующей оболочки класса?
public class UniversalRealTimeDataSource : DataSource<Identifier> {
public double GetValue(Identifier id) {
// there would have to be code in here that says "if(id is FooIdentifier) ... else ...
// which is (IMO) an anti-pattern so, how to avoid this?
}
}
Редактировать: Я старался как можно больше сохранить гарантии безопасности типа времени компиляции. Это было бы довольно просто с некоторыми if(!(id is FooIdentifier))
кодом исключения исключения, но я хочу сделать невозможным это во время компиляции.
Итак, если я правильно понял, пытаетесь ли вы найти способ связать FooDataSource и FooIdentifier во время компиляции, не проверяя тип явно в методе GetValue? –
Я не совсем уверен, что я понимаю вопрос, но это _seems_, как будто вы можете задавать что-то похожее на вопрос, который я ответил ранее на этой неделе: http://stackoverflow.com/a/35979564. Ваш вопрос может даже рассматриваться как дубликат этого, хотя и сформулированный гораздо более неясным образом. –