面向对象OLAP的灵活标签系统

在数据分析和商业智能领域,OLAP(在线分析处理)是一种重要的技术,它允许用户从多个维度对数据进行分析。然而,现有的OLAP实现往往在处理复杂场景时显得不够灵活。本文将介绍一种面向对象的OLAP标签系统,它通过为实体赋予不同的标签值,实现了对不同实体状态的同时记忆和灵活切换。

在尝试寻找面向对象OLAP的资料时,发现相关的文档和资源非常有限。例如,在亚马逊网站上搜索“面向对象OLAP”时,只能找到21个相关项目,而且这些项目的文档质量参差不齐。

使用代码

本文中展示的代码仅用于说明目的,不期望它是完美的。实际上,预计会有更好的实现方式。

核心概念

假设需要开发一个应用程序,它根据不同实体的特定值进行计算。这些值可以是真实的,比如支付的金额或销售的商品数量,也可以是虚构的。虚构的值可以有多种类型,比如“最坏情况”或“所有交易只使用一个银行”等。有些组合是计划内的,即根据它们做出预期;其他组合则作为选择最优解所需的原材料。无论如何,OLAP“立方体”应该是灵活的。

简单来说,每个可以为建模目的而改变的实体都需要同时记住不同的状态。为了方便用户,用户可以选择特定实体的特定状态,并查看它如何影响结果。作为解决方案,建议(目前)使用“标签”,这些标签将是访问特定实体状态的标记。

实现

首先,声明一个实体的接口:

public interface IBonus { double Amount { get; set; } DateTime Date { get; set; } }

这是一个简单的例子:在特定日期,一个人将收到多少(假设是美元)的奖金。

现在,为什么这个人不是接口的一部分?为什么接口看起来不像这样:

public interface IBonusNotImplemented { double Amount { get; set; } DateTime Date { get; set; } string Person { get; set; } }

答案很简单:因为正在建模奖金的大小和日期,而不是人。毕竟,如果想让人没有奖金,总是可以通过标记Person实体来实现。

回到正题。定义一个通用接口,用于所有支持标签的实体:

public interface ILabeled { string GetCurrentLabel(); void SetCurrentLabel(string label); void AddLabel(string label); ICollection Labels { get; } T GetEntityByLabel(string label); }

标签可以是一个比字符串更复杂的对象,但即使是字符串也非常有用。注意这里没有RemoveLabel方法。那是因为不想在这里实现它,因为有一些含义尚未完全理解。

在这里,声明一个新的接口,它实现了前两个接口:

public interface IBonusLabeled : IBonus, ILabeled { }

声明将实现所需功能的具象类:

internal class Bonus : IBonus { private double amount_; private DateTime date_; public double Amount { get { return amount_; } set { amount_ = value; } } public DateTime Date { get { return date_; } set { date_ = value; } } } internal class BonusLabeled : Labeled, IBonusLabeled { private string person_; public string Person { get { return person_; } set { person_ = value; } } public double Amount { get { return base.Current.Amount; } set { base.Current.Amount = value; } } public DateTime Date { get { return base.Current.Date; } set { base.Current.Date = value; } } public new IBonusLabeled GetEntityByLabel(string label) { return base.GetEntityByLabel(label) as IBonusLabeled; } }

Labeled是所有魔法发生的地方:

public class Labeled : ILabeled where T : class, new() { private Dictionary entities_; private string currentLabel_; public Labeled() { entities_ = new Dictionary(); } public string GetCurrentLabel() { return currentLabel_; } public void SetCurrentLabel(string label) { if (!entities_.ContainsKey(label)) { // throw a nasty exception or do nothing or... } currentLabel_ = label; } public void AddLabel(string label) { if (entities_.ContainsKey(label)) { // throw a nasty exception or do nothing or... } entities_.Add(label, new T()); } public ICollection Labels { get { return entities_.Keys; } } public T GetEntityByLabel(string label) { // This piece of code has to be implemented separately, it is not a simple // thing, but is unimportant here. Yeah, I'm being lazy :) return MagicFactory.CreateNewLabeledProxy(this, label); } protected T Current { get { return entities_[currentLabel_]; } } }

BonusLabeled是Bonus集合的代理。MagicFactory将创建一个新的BonusLabeled对象,它将有另一个标签作为当前标签,但将共享相同的entities_集合。

不幸的是,这还不是全部。例如,可能希望有些字段被标记,有些则没有。比如Person字段(见下文)。

public interface IBonusLabeled : IBonus, ILabeled { string Person { get; set; } }

上述代码表明(或应该读作):Person不会被标记。在现实世界中,这可能意味着要么支付奖金,要么不支付。一个特定的奖金属于一个特定的人。那么,问题是什么?问题是“共享”的Person字段值必须以这样的方式实现,以至于不能突然出现这种情况:

IBonusLabeled bonusA = new BonusLabeled { Person = "Smith" }; string labelA = "what-if-we-pay-standard"; bonusA.AddLabel(labelA); bonusA.SetCurrentLabel(labelA); bonusA.Amount = 10.0; bonusA.Date = DateTime.Now; string labelB = "what-if-we-pay-more"; bonusA.AddLabel(labelB); IBonusLabeled bonusB = bonusA.GetEntityByLabel(labelB); bonusB.Person = "Clay"; bool equals = bonusB.Person == bonusA.Person; if (!equals) { // terrible things }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485