我正在设计一个适用于多个数据源的系统。这些数据源具有可在创建时对其进行某些检查的标识符。它允许获取历史和实时数据。我目前的抽象涉及三个基类。
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)
}
}
问题(最后)是如何创建一个实际遵循OOP主体来填写以下类shell的类?
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))
抛出异常类型的代码,这将是相当简单的,但我想让它在编译时不可能发生。
答案 0 :(得分:0)
我的最终解决方案在编译时类型安全方面有点妥协。标识符从数据源菜单(通用数据源)中选择自己的数据源。除非程序员错误地使用代码,否则这可以防止运行时错误。很多东西可能都是私有的,公共层将包含所有DataContext和Identifier子类。
public abstract class DataSource
{
// base class for all data sources
// maintains connections opening and closing plus
// thread safety concerns
// these classes will most likely be private
// maybe even within the universal data source class as inner classes
// THE TWO METHODS BELOW ARE ONLY TO BE CALLED FROM WITHIN THE UniversalDataSource CLASS
public abstract double GetRealTimeValue(Identifier id);
public abstract double GetHistoricalValue(Identifier id, DateTime asof);
}
public class FooDataSource : DataSource {
public override double GetRealTimeValue(Identifier id)
{
return -1;
// real implementation here, must be identifier type upcasting with runtime check
}
public override double GetHistoricalValue(Identifier id, DateTime asof)
{
return -2;
// real implementation here, must be identifier type upcasting with runtime check
}
}
public class BarDataSource : DataSource {
public override double GetRealTimeValue(Identifier id)
{
return -3;
// real implementation here, must be identifier type upcasting with runtime check
}
public override double GetHistoricalValue(Identifier id, DateTime asof)
{
return -4;
// real implementation here, must be identifier type upcasting with runtime check
}
}
/// <summary>
/// holds initialized references to all possible data sources
/// </summary>
public class UniversalDataSource
{
public FooDataSource FooDS { get; internal set; }
public BarDataSource BarDS { get; internal set; }
public UniversalDataSource(FooDataSource fooDs, BarDataSource barDs)
{
this.FooDS = fooDs;
this.BarDS = barDs;
}
public double GetRealTimeValue(Identifier id)
{
var specificDS = id.GetDataSource(this);
return specificDS.GetRealTimeValue(id);
}
public double GetHistoricalValue(Identifier id, DateTime asof)
{
var specificDS = id.GetDataSource(this);
return specificDS.GetHistoricalValue(id, asof);
}
}
public abstract class Identifier
{
public string Value { get; internal set; }
public Identifier(string value)
{
Value = value;
}
/// <summary>
/// returns the appropriate data source for THIS kind of identifier (abstractly)
/// </summary>
/// <param name="universalDataSource"></param>
/// <returns></returns>
public abstract DataSource GetDataSource(UniversalDataSource universalDataSource);
}
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 override DataSource GetDataSource(UniversalDataSource universalDataSource)
{
return universalDataSource.FooDS;
}
}
public class BarIdentifier : Identifier
{
public BarIdentifier(string value) : base(value)
{
// checks on the string values that come in for the Bar
// source
}
public override DataSource GetDataSource(UniversalDataSource universalDataSource)
{
return universalDataSource.BarDS;
}
}
public abstract class DataContext
{
public UniversalDataSource DataSource { get; internal set; }
protected DataContext(UniversalDataSource dataSource)
{
DataSource = dataSource;
}
public abstract double GetValue(Identifier id);
}
public class RealTimeDataContext : DataContext {
public RealTimeDataContext(UniversalDataSource dataSource) : base(dataSource)
{
}
public override double GetValue(Identifier id)
{
return DataSource.GetRealTimeValue(id);
}
}
public class HistoricalDataContext : DataContext {
public DateTime AsOf { get; internal set; }
public HistoricalDataContext(UniversalDataSource dataSource, DateTime asof) : base(dataSource)
{
AsOf = asof;
}
public override double GetValue(Identifier id)
{
return DataSource.GetHistoricalValue(id, AsOf);
}
}
[TestFixture]
public static class TestMe
{
[Test]
public static void MyTest()
{
// create the data context (to get data from)
var ds = new UniversalDataSource(
new FooDataSource(),
new BarDataSource()
);
var realTimeDataContext = new RealTimeDataContext(ds);
var historicalDataContext = new HistoricalDataContext(ds, DateTime.MinValue);
var fooId = new FooIdentifier("onetuhoenthuo");
var barId = new BarIdentifier("onetuhoenthuo");
// testing dispatch
Assert.AreEqual(-1, realTimeDataContext.GetValue(fooId));
Assert.AreEqual(-2, historicalDataContext.GetValue(fooId));
Assert.AreEqual(-3, realTimeDataContext.GetValue(barId));
Assert.AreEqual(-4, historicalDataContext.GetValue(barId));
}
}
也许这个解决方案可以清除我从一开始就尝试做的事情。数据上下文类甚至存在的原因是由于处理财务数据,周末和假日可能导致NA值和不同标识符的混洗日期在历史数据请求设置中可能有意义。这些策略将通过覆盖GetValue
方法以多态方式实现。