包含其他类集合的类的设计(操作方法)

时间:2010-06-25 09:29:36

标签: design-patterns class-design abstraction

如何设计涉及其他类集合的类?

一般例子:

工作区包含多个项目 项目包含大量资源 每个资源可能包含大量文件

所以这里确定的类可以是Workspace,Project,Resource和File。 Workspace将包含Project.Project列表,其中包含Resources和Resource列表。当然每个班级都有相关的设置。

现在的基本问题是:
a)谁创建并为特定集合添加了一个类?另一个类或包含该集合的类?
b)如何跟踪特定的集合以及如何存储它们? c)谁审核了特定馆藏的变化? d)在这种情况下可以应用哪些不同的设计模式?

基本上我想减少不同类之间的耦合。

谢谢大家

3 个答案:

答案 0 :(得分:5)

有很多种关系 - 考虑

  • 汽车和轮子
  • 汽车和司机
  • 汽车和注册所有者
  • 客户及订单和订单行
  • 学校和班级以及班级实例

如果你看一下UML建模,你会看到诸如基数和方向以及Aggegration和Composition之间的distictions以及与相关对象的生命周期有关的问题等概念。

然而,我们需要一系列技术和模式来处理不同类型的关系,这并不足为奇。

关于d)。有一个最重要的原则Law of Demeter或最少知识的原则。

然后,一个重要的技术是封装通过隐藏信息来减少耦合。汽车可能对人的许多细节没什么兴趣,所以我们可能在Person类上有一个IDriver接口,IDriver提供了汽车关心的特定方法。一般原则是支持编程到接口。

在此之后,我们可以考虑a)。创建。由于我们倾向于使用Interfaces,因此使用Factory模式通常是有意义的。这确实留下了谁叫工厂的问题。我们更喜欢:

   IPerson aPerson = myAutomobile.createDriver( /* params */);  

  IPerson aPerson = aPersonFactory.create( /* params */);
  myAutomobile.addDriver(aPerson);

在这里我认为很明显,汽车对人们了解不多,因此第二是更好的责任分工。但是Orders可以合理地创建OrderLines,Classes创建ClassInstances?

B)。注意动向?这就是我们拥有丰富的Collection类集的原因。使用哪些取决于关系的性质(一对一,一对多;等等)以及我们如何使用它。所以我们根据需要选择Arrays和HashMaps等。对于汽车/轮子,我们甚至可以使用汽车的名称属性 - 毕竟汽车有六个轮子(frontLeft,frontRight,backLeft,backRight,备用和转向)。如果用“store”表示持久化,那么我们在关系数据库中查看外键等技术。 RDBMS和内存中对象之间的映射越来越多地通过诸如JPA之类的良好持久性机制来管理。

C)。审计?我没有看到在关系级别专门应用审计。显然,automobile.addDriver()方法可能是任意复杂的。如果审计此操作有业务需求,那么很明显这是一个体面的地方。这只是围绕谁拥有这些信息的标准OO设计问题。一般原则:“不要重复自己”非常清楚我们不希望每个调用addDriver()的对象都需要记住审计,因此它是Auto的工作。

答案 1 :(得分:2)

在设计软件时,我发现从类型理论的角度来看事物是很有用的,看看它在哪里引导我。

WorkSpace的类型为项目+项目^ 2 +项目^ 3 ... (意味着项目列表的 true true < / em> of WorkSpace)

类似地,

项目类型为资源+资源^ 2 +资源^ 3 ...

资源的类型为文件+文件^ 2 +文件^ 3 ...

因此,在像C#†这样的语言中,您可能会定义WorkSpace类:

public class WorkSpace : IList<Project> //the important thing here is that you're declaring that things that are true for a list of Projects is true for a WorkSpace. The WorkSpace class may in fact do other stuff too...
{

}

和其他类似。

然后在您的客户端代码中,您将使用它:

foreach (var project in WorkSpace)
{
    //do stuff
}

Projects.Add(new Resource() { file1, file2, file3, file4, /* etc */});

首先考虑类型及其关系。封装是一种低级管家概念。其原则是将相关代码保持在一起并且不相关的代码相隔很远(远距离意味着在某种边界之间或之后分隔,例如函数或类或语言可能提供的任何其他边界概念)。

†根据您的发布历史记录,我猜测您熟悉C#,但这适用于其他语言。

答案 2 :(得分:1)

创造良好的面向对象设计是一种艺术形式,但有一些原则需要牢记。

考虑一下您的用户将如何使用您的界面。 谁是你的用户?它只是你,为了特定的目的,还是你为不同的客户设计的东西。界面设计很难,但要想一想用户实际想要如何使用界面的真实示例。一个很好的方法是编写测试,这会迫使你使用自己的东西。另一种方法是查看执行相同操作的现有库,并查看它们的使用方式。

保持简单,愚蠢。 也就是说,从你的来电者的角度来看,保持简单。这适用于封装,您只应根据需要公开实现的内部。它适用于创建易于理解的一致界面。这意味着你应该避免为每个可以设想的阶级制造一个物体的陷阱,制造大量的属性,并尽可能地保持一般性。最后一点是特别重要的一点;让我们成为开发人员的好处是我们有能力进行推测和抽象,但正因为如此,我们常常陷入使系统变得比他们需要的更复杂的陷阱。

考虑所有权。 每个对象将由另一个对象拥有,这意味着它应该作为该对象的一部分创建和销毁,或者它将被另一个对象引用。您应该相应地设计您的界面。所有权是一种耦合形式,但它通常是正确的设计。它当然简化了您的界面,以及调用者维护自己对象的负担。

虽然知道并使用你的集合库。 了解您正在使用的任何语言/框架的集合库,并使用它们。此外,尝试在命名和行为方面使您自己的接口与这些库一致。

关于审核。 您可以在类中进行一些审计(记录,保持统计),但如果您的审计需求很复杂(需要知道数据何时发生变化),那么使用Observer模式可能会更好。