C# 强相关类和类型安全抽象
我正在设计一个可以处理多个数据源的系统。这些数据源具有标识符,可以在创建它们时对其进行某些检查。它允许获取历史和实时数据。我当前的抽象涉及三个基类 DataSource类负责连接到服务,并维护该数据源的机密信息,包括如何打开和关闭连接以及线程安全问题 DataContext类负责获取哪些值,无论是实时值还是历史值,以及在什么日期等,在其他上下文中,您可能需要混合历史日期(参数化),这就是为什么我希望使用多态性来实现这一点 Identifier类负责解析字符串并对正则表达式进行验证,以确保传入的字符串标识符至少在某种程度上是有效的。它还用于类型安全,因为不允许将一个数据源的标识符传递到另一个数据源 参见下面的示例代码C# 强相关类和类型安全抽象,c#,C#,我正在设计一个可以处理多个数据源的系统。这些数据源具有标识符,可以在创建它们时对其进行某些检查。它允许获取历史和实时数据。我当前的抽象涉及三个基类 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)
}
}
公共类数据源
{
//所有数据源的基类
//保持连接打开和关闭
//线程安全问题
}
公共类FoodDataSource:DataSource{}
公共类BarDataSource:数据源{}
公共抽象类标识符
{
公共字符串值{get;内部集;}
公共标识符(字符串值)
{
价值=价值;
}
}
公共类标识符:标识符
{
公共标识符(字符串值):基(值)
{
//在输入的字符串上进行检查
//特定于foo数据源
}
}
公共类标识符:标识符
{
公共BarIdentifier(字符串值):基(值)
{
//检查为条输入的字符串值
//来源
}
}
公共抽象类DataContext,其中TIdentifier:标识符
{
公共摘要双GetValue(TIdentifier id);
}
公共抽象类FooDataContext:DataContext{}
公共抽象类BarDataContext:DataContext{}
公共类FooRealTimeDataContext:FooDataContext
{
公共覆盖双GetValue(FooIdentifier id)
{
//这里真正的实施
返回-1;
}
}
公共类BarRealTimeDataContext:BarDataContext
{
公共覆盖双GetValue(BarIdentifier id)
{
//这里真正的实施
返回-10;
}
}
[测试夹具]
公共静态类TestMe
{
[测试]
公共静态void MyTest()
{
//创建数据上下文(从中获取数据)
var ctx=新的FooRealTimeDataContext();
ctx.GetValue(新的FooIdentifier(“onetuhoenthuo”);//编译(良好)
//ctx.GetValue(新的BarIdentifier(“noetuhneoth”);//无法编译(也很好)
}
}
问题(最后)是如何创建一个实际遵循OOP原则的类来填充下面的类外壳
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?
}
}
公共类UniversalRealTimeDataSource:DataSource{
公共双GetValue(标识符id){
//这里必须有一段代码,上面写着“如果(id是foodidentifier)…否则。。。
//哪一种(IMO)是反模式?那么,如何避免这种情况?
}
}
编辑:我一直在尽可能地保持编译时类型的安全保证。如果(!(id是foodidentifier))有一些
,这将是相当简单的
抛出异常类型的代码,但我想使其不可能在编译时发生。我的最终解决方案在编译时类型安全性方面有点折衷。标识符从数据源菜单中选择自己的数据源(通用数据源)。这可以防止运行时错误,除非程序员错误地使用代码。许多内容可能会被设置为私有,公共层将包括所有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));
}
}
公共抽象类数据源
{
//所有数据源的基类
//保持连接打开和关闭
//线程安全问题
//这些课程很可能是私人的
//甚至可能在通用数据源类中作为内部类
//下面的两个方法只能从UniversalDataSource类中调用
公共抽象双GetRealTimeValue(标识符id);
公共抽象双GetHistoricalValue(标识符id、日期时间asof);
}
公共类FoodDataSource:DataSource{
公共重写双GetRealTimeValue(标识符id)
{
返回-1;
//这里的实际实现必须是标识符类型的向上转换和运行时检查
}
公共重写双GetHistoricalValue(标识符id、日期时间asof)
{
返回-2;
//这里的实际实现必须是标识符类型的向上转换和运行时检查
}
}
公共类BarDataSource:数据源{
公共重写双GetRealTimeValue(标识符id)
{
返回-3;
//这里的实际实现必须是标识符类型的向上转换和运行时检查
}
公共重写双GetHistoricalValue(标识符id、日期时间asof)
{
返回-4;
//这里的实际实现必须是标识符类型的向上转换和运行时检查
}
}
///
///保存对所有可能数据源的初始化引用
///
公共类通用数据源
{
公共FoodDataSource FooDS{get;internal set;}
公共BarDataSource BARD{get;内部集合;}
公共通用数据源(FoodDataSource fooDs,BarDataSource barDs)
{
这就是食物;
这个。吟游诗人=吟游诗人;
}
公共双GetRealTimeValue(标识符id)
{
var specificDS=id.GetDataSource(此);
返回specificDS.GetRealTimeValue(id);
}
公共双GetHistoricalValue(标识