2016-03-18 2 views
0

Я разрабатываю систему, которая работает с несколькими источниками данных. Эти источники данных имеют идентификаторы, которые могут выполнять определенные проверки при их создании. Это позволяет получать исторические данные и данные в реальном времени. Моя текущая абстракция включает в себя три базовых класса.Сильно связанные классы и типы безопасных абстракций

Класс 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)) кодом исключения исключения, но я хочу сделать невозможным это во время компиляции.

+0

Итак, если я правильно понял, пытаетесь ли вы найти способ связать FooDataSource и FooIdentifier во время компиляции, не проверяя тип явно в методе GetValue? –

+0

Я не совсем уверен, что я понимаю вопрос, но это _seems_, как будто вы можете задавать что-то похожее на вопрос, который я ответил ранее на этой неделе: http://stackoverflow.com/a/35979564. Ваш вопрос может даже рассматриваться как дубликат этого, хотя и сформулированный гораздо более неясным образом. –

ответ

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.

Смежные вопросы