反向工程:如何表示 C# 字典(Dictionary)?

3

我已经编写了代码,但为了自己的知识,我想知道如何对其进行建模。

接下来是代码部分:一个用户可以拥有多个配置文件,每个配置文件仅与一个应用程序相关联。 配置文件定义了用户在应用程序中的权限:同一用户可以同时在 app1 的范围内成为管理员,同时也是 app2 的普通用户…

伪代码如下:

public class User
{
   public string UserId { get; set; }

   // Only store the AppId of application but it could be an IDictionary<Application, UserProfile>
   public IDictionary<string, UserProfile> Profiles { get; set; }
}

public enum UserProfile 
{
   BasicUser,
   Administrator,
}

public class Application
{
   public string AppId { get; set; }
}

如您所见,UserProfile只是一个枚举类型,而Application可以是一个简单字符串,但我需要在这里使用类。
我该如何在类图中表示这三个实体?我认为类关联不是一个好的选项,因为它没有任何属性。但是我不知道还有什么其他可能性。
3个回答

2
我在你的代码中没有看到任何n元关联。
你所描绘的情况如下图所示: enter image description here Application 没有与任何东西相关联,因此它是一个孤儿。
属性可以用不同的方式表示,也与语言有关。上述内容适用于C#。

感谢您的努力。如果profiles是一个 <Application,UserProfile> 字典而不是 <string,UserProfile>,这两个类将如何相关? - paradise
1
在这种情况下,您可以像枚举一样向Application绘制依赖关系。 - qwerty_so
你的模型没有将UserApplication之间的关系作为一个关联(类)来捕捉。此外,在C#实现模型中,复制类矩形中的属性是一种非常丑陋/嘈杂的方法。可以使用<<get/set>>属性构造型更好地表达这一点,请参见https://dev59.com/0l4c5IYBdhLWcg3wQYSC#28141950。 - Gerd Wagner
@GerdWagner 你可能是对的。当回答这样的问题时,我的意图并不是要给出一个完全深入的模型(这是不可能的,需要进行广泛的讨论)。更多的是我试图指向某个方向。看看Christophe的回答:老实说,我懒得详细解释那么多。我只能猜测这是为了某种考试而提问,而提问者正在准备这些考试。帕累托法则。 - qwerty_so

2

字典很重要!

起初我想的像qwerty_so,但是:

  • UserProfiles有一个合格的关联:它用IDictionary实现,其中使用string来限定User
  • 这种关联是单向导航的,因为你可以轻松地从UserUserProfile,但是反过来就更困难了(虽然可以,但必须遍历所有的用户)。

因此,图表可能如下所示:

enter image description here

关于这张图,还有一些额外的注释:
  • 对于 C# 属性,不存在 UML 标准,它们是 UML 属性和操作(通过隐式 getter 和 setter)的混合体。通常做法是使用定义了 stereotype 为 «property» 的 UML 剖面。我的首选选项是将其保留在类的属性中(例如 herehere),因为这是 C# 属性的语义。然而,一些人认为这个构造应该被视为 UML 操作的特殊化(例如 here 和 qwerty 的回答),这也是一个有效的论点,特别是如果您定义了自定义 getter 和 setter。
  • 在限定关联中要注意多重性,因为它适用于以字符串为限定符的 User。所以 0..1 UserProfile 是针对一个特定的 User 和字符串组合。如果考虑未限定的 User,实际上是 0..*。

模型转换

我们必须记住,一个给定的实现可能对应不同的设计。实现是指你写代码的方式,而设计是指你看待事物的方式。
因此,我们可以将这个反向工程模型转换为符合您的思维模型。只有您能告诉我们您想要如何看待导致当前实现的事物。
例如,您可能对可导航性不太感兴趣,而将重点放在未经资格认证的“用户”和“用户配置文件”之间的多对多关联上。采用这种方法,被用作限定符的字符串将变成关联类的属性:

enter image description here

请注意,这是一种转型。合格的关联和一对多不是同一件事,两种模型并不完全相同。
接下来是n元关联!
现在关键问题是该关联类中的字符串属性的目的。我强烈怀疑它实际上是您的应用程序的id(请在评论中确认)。
如果是这种情况,我们将有一个关联类与应用程序之间的关联。并且我们可以从关联类中删除字符串属性,因为它将变得多余。
但是,一个与其他类相关联的关联类表明实际上存在n-ary associaton(这里是三元)。因此,我们可以明确这一点:

enter image description here

现在你有一个n元关联。如果每个三元组(用户、应用程序、用户配置文件)需要关联的其他属性,则甚至可以在其后面拥有一个关联类。
但要注意,这张图不太精确。例如,在n元关联中更难指示可导航性约束。它也不再完全对应于您的实现:该模型表明您可以从应用程序中找到用户,而在您的实现中,这更困难。
结论/建议:
个人而言,我发现n元关联很难处理,我更喜欢将它们分解为一组类和二元关联(必要时使用关联类):这可以消除许多歧义并提供更严格的控制。我只能向您推荐同样的方法。
还要考虑您的实现:如果实现代表您对世界的看法,请使用限定关联,因为它是UML与字典的等效项。
但是,如果三元关联是您对世界的看法,则您可能迟早需要重新审视当前的实现。您可能会提取配置文件以创建自己的类。

1
我喜欢你的n元关联,它几乎就像我在提问时想象的设计一样。但是你指出它并不完全对应我的实现,我认为你是正确的!可导性是关键。为了匹配这个图表,我也应该在我的应用程序类中拥有一个用户/配置文件字典。由于情况并非如此,限定关联似乎是最佳选择。谢谢。 - paradise
是的,在您的第一个图表中,:stringAppId 相同。我编辑了我的问题以改进 Id 的命名。 - paradise
你的模型没有将UserApplication之间的关系作为一个关联(类)来捕捉。同时请注意,“世界的视角”更适用于(逻辑)设计模型,而不是使用映射/字典实现多值属性的实现选择。 - Gerd Wagner
在n元关联中,用户始终与应用程序以及用户配置文件相关联。这意味着您可以为用户查找应用程序或为应用程序查找用户。在n元关联背后,您也可以像在二元关联背后一样拥有一个关联类。我没有展示它,因为当前的代码不需要它。实际上,如果存在(但代码中没有证据)与应用程序无关的属性(例如用户首选项),我们可以考虑用户和应用程序之间的额外二元关联。 - Christophe
@GerdWagner 我完全同意你的第二点。事实上,我采用了一个逐步的方法,给予 OP 在实现模型和设计模型之间进行选择的机会(尽管仍然假定后者更有用)。 - Christophe

2
您的C#属性User::Profiles实现了一个多对多关联类,该类在UserApplication之间具有一个UML属性,如role: RoleEL,其中我用"RoleEL"替换了您的枚举名称"UserProfile"以获得更好的可读性(请注意,"EL"代表"枚举文字")。这在以下类图中显示:

enter image description here

我认为这种设计模型比@qwerty_so和@Christophe提出的模型更简单、更自然。

请注意,qwerty_so的模型不是(逻辑)设计模型,而是实现模型,因为它没有显示UserApplication之间的关系,而是将其隐藏在profiles属性的代码中。此外,它不必要地重复了一个UML属性(使用令人困惑的构造关键字«property»)。


我也喜欢这个。现在我不知道该选择你的提议还是@Christophe的第一个提议,但需要注意将“UserProfile”和“Application”进行切换。 - paradise
@paradise:请注意,@Christophe的模型没有正确地将UserApplication之间的关系建模为一个关联(类)。 - Gerd Wagner
1
@paradise:还要注意,我的模型允许通过添加其他属性(例如使用时间)来扩展用户应用程序配置文件。 - Gerd Wagner
1
是的,这也是一个不错的模型。对于正向工程,我可能会从那里开始。在我的回答中,我很想展示第二步是这样的。但我没有这样做,因为问题是关于反向工程的,我想要贴近这个思考过程。此外,在用户管理领域,应用程序角色通常比枚举更复杂(例如,动态创建,与功能或数据约束相关联)。这个模型不会以同样的方式承认角色作为独立于用户的实体的存在。但是+1,因为它是一个有效的替代方案。 - Christophe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接