包含其他类集合的类的设计(操作方法)
如何设计涉及其他类集合的类?
一般示例:
工作区包含多个项目。
一个项目包含大量资源。
每个资源可能包含大量文件。
所以这里标识的类可以是 Workspace、Project、Resource 和 File。 工作区将具有项目列表。项目将具有资源列表,资源将具有文件列表。当然每个类都有其相关的设置。
现在的基本问题是:
a) 谁创建类并将其添加到特定集合中?另一个类或包含该集合的类?
b) 另外如何跟踪特定集合以及如何存储相同集合?
c) 谁审核特定集合的更改?
d) 在这种情况下可以应用哪些不同的设计模式?
基本上我想减少不同类之间的耦合。
谢谢大家
How to design classes involving collections of other classes?
General Example:
A Workspace contains number of Projects .
A Project contains large number of Resources .
Each Resource may contain large number of Files.
So here the classes identified can be Workspace,Project,Resource and File.
Workspace will have list of Project.Project will have list of Resources and Resource will have list of Files. Of course each class has its related settings.
Now the basic questions are :
a) Who creates and adds a class to a particular collection? Another class or the class containing the collection?
b) Also how to keep track of a particular collection and how to store same?
c) Who audits changes to a particular collection?
d) What are the different design patterns that could be applied in such situations?
Basically I want to reduce the coupling between the different classes .
Thanks Everyone
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
有很多种关系 - 考虑
实例。如果您查看 UML 建模,您将看到诸如基数和方向之类的概念以及它们之间的区别。聚合和组合以及与相关对象的生命周期相关的问题。
因此,我们需要一系列技术和模式来处理不同类型的关系也就不足为奇了。
关于d)。有一个压倒一切的原则德米特法则或最少知识原则。
一项重要的技术是封装通过隐藏信息来减少耦合。汽车可能对人的许多细节不感兴趣,因此我们的 Person 类上可能有一个 IDriver 接口,IDriver 提供了汽车关心的特定方法。一般原则是支持接口编程。
接下来,我们可以考虑 a)。创建。由于我们倾向于使用接口,因此使用工厂模式通常是有意义的。这就留下了谁给工厂打电话的问题。我们更喜欢:
或者
在这里,我认为很明显,汽车不太了解人,因此第二个是更好的职责分工。然而,也许订单可以合理地创建订单行,类可以合理地创建类实例?
b).跟踪?这就是为什么我们有丰富的 Collection 类集。使用哪些取决于关系的性质(一对一、一对多等)以及我们如何使用它。所以我们根据需要选择Arrays和HashMaps等。对于汽车/车轮,我们甚至可以使用汽车的名称属性 - 毕竟汽车有六个轮子(左前、右前、左后、右后、备用和转向)。如果“存储”是指持久存在,那么我们正在研究关系数据库中的外键等技术。 RDBMS 和内存中对象之间的映射越来越多地由 JPA 等良好的持久性机制来管理。
c).审计?我还没有看到审计专门应用于关系层面。显然,automotive.addDriver() 方法可能任意复杂。如果业务需要审核此操作,那么很明显这是一个不错的地方。这只是一个标准的面向对象设计问题,围绕着谁拥有信息。一般原则:“不要重复自己”,很明显,我们不希望每个调用 addDriver() 的对象都需要记住审核,因此这是 Auto 的工作。
There are many kinds of relationships - consider
If you look at UML modelling you'll see concepts such as Cardinality and Direction and distictions between Aggegration and Composition and questions relating to the life-cycle of the related objects.
It's then unsurprising that we need a range of techniques and patterns to deal with different kinds of relationships.
Concerning d). There's one overriding principle Law of Demeter or principle of least knowledge.
One important technique is then, Encapsulation decrease coupling by hiding information. Automobiles probably have little interest in many details of people, so we might have a IDriver interface on our Person class, IDriver offers the particular methods that Automobile cares about. The general principle being to favour programming to interfaces.
Following that through, we can think about a). Creation. As we're tending to use Interfaces, it often makes sense to use Factory patterns. That does leave the question of who calls the factory. Do we prefer:
or
Here I think it's pretty clear that Automobiles don't know much about people, and therefore the second is better division of responsibilities. However maybe Orders could reasonably create OrderLines, Classes create ClassInstances?
b). Keeping track? That's why we have rich sets of Collection classes. Which ones to use depend upon the nature of the relationship (one-one, one-many; etc.) and how we use it. So we pick Arrays and HashMaps etc. according to need. For a Car/Wheel we might even use names attributes of the Car - after all a Car has exactly six wheels (frontLeft, frontRight, backLeft, backRight, spare and steering). If by "store" you mean persist, then we're looking at techniques such foreign keys in a relational database. Mapping between RDBMS and in-memory objects is increasingly managed by nice persistence mechanisms such as JPA.
c). Audit? I've not seen auditing applied specifically at a relationship level. Clearly the automobile.addDriver() method may be arbitrarily complex. If there's a business requirement to audit this action, then it's pretty clear that this a decent place to do it. This is just a standard OO design question revolving around who owns the information. General principle: "Do Not Repeat Yourself" so pretty clearly we don't want every object that calls addDriver() to need to remember to audit, hence it's Auto's job.
在设计软件时,我发现从类型论的角度看待事物并看看它会引导我走向何方很有用。
工作空间的类型为 Project + Project^2 + Project^3...(这意味着项目列表中 true 的任何内容都是 true工作区)
同样,
项目的类型为资源 + 资源^2 + 资源^3...
资源的类型为文件 + 文件^2 + 文件^3 ...
因此,在像 C#† 这样的语言中,您可以这样定义 WorkSpace 类:
对于其他类也类似。
然后在您的客户端代码中,您将像这样使用它:
或者
首先考虑类型及其关系。封装是一个低级的内务处理概念。其原则是使相关代码靠近,使不相关代码远离(其中远离意味着被某种边界分隔或在某种边界后面,例如函数或类或语言可能提供的任何其他边界概念)。
†从您的发帖历史来看,我推测您熟悉 C#,但这也适用于其他语言。
When designing software I find it useful to look at things from a type theoretic point of view and see where it leads me.
A WorkSpace is of type Project + Project^2 + Project^3... (meaning whatever is true of a list of projects is true of a WorkSpace)
Similarly,
A Project is of type Resource + Resource^2 + Resource^3...
A Resource is of type File + File^2 + File^3 ...
So in a language like C#† you might define you WorkSpace class thusly:
and similarly for the other classes.
Then in your client code you'd use it like this:
or
Think about the types and their relationships first. Encapsulation is a bit of a low-level housekeeping concept. The principle there is to keep related code close together and unrelated code far apart (where far apart means separated by or behind some kind of boundary, e.g. a function or a class or whatever other boundary concept a language might offer).
†From your posting history I surmise that you are familiar with C# but this applies to other languages.
创建良好的面向对象设计有点像一种艺术形式,但有一些原则需要牢记。
考虑您的用户将如何使用您的界面。
谁是你的用户?仅仅是您为了特定目的而设计,还是您正在为不同的客户设计一些东西。界面设计很困难,但请考虑用户实际希望如何使用界面的真实示例。做到这一点的一个好方法是编写测试,这迫使您使用自己的东西。另一种方法是查看执行相同操作的现有库并了解它们的使用方式。
让事情变得简单、愚蠢。
也就是说,从呼叫者的角度来看,请保持简单。这适用于封装,您应该只根据需要公开实现的内部结构。它适用于创建易于理解的一致界面。这意味着您应该避免陷入为每个可以想象的类创建对象、创建大量属性并尽可能通用的陷阱。最后一点尤其重要;作为开发人员,我们之所以优秀,是因为我们有推测和抽象的能力,但正因为如此,我们经常陷入让系统变得比实际需要更加复杂的陷阱。
考虑所有权。
每个对象要么被另一个对象拥有,这意味着它应该作为该对象的一部分被创建和销毁,要么被另一个对象引用。您应该相应地设计您的界面。所有权是一种耦合形式,但它往往是正确的设计。它无疑简化了您的界面,并减轻了调用者维护自己的对象的负担。
尽管应该了解并使用你的收藏库。
了解您正在使用的任何语言/框架的集合库,并使用它们。另外,尝试使您自己的接口在命名和行为方面与这些库保持一致。
关于审计。
您可以在类中进行一些审核(记录、保存统计数据),但如果您的审核需求很复杂(需要知道数据何时更改),那么使用观察者模式可能会更好。
Creating good OO design is a bit of an artform, but there are some principals to keep in mind.
Think about how your users will use your interface.
Who is your user? Is it just you, and for a specific purpose, or are you designing something for different clients. Interface design is hard, but think about real examples of how your users will actually want to use your interface. One good way to do this is by writing tests, which forces you to use your own stuff. Another way is to look at existing libraries that do the same thing and see how they are used.
Keep things simple, stupid.
That is, keep them simple from the perspective of your callers. This applies to encapsulation, where you should only expose the internals of your implementation as needed. It applies to creating a consistent interface that is easy to understand. And it means you should avoid the trap of making an object for every conceivable class, making tons of attributes, and being as general as possible. This last point is an especially important one; what makes us good as developers is that we have the ability to speculate and abstract, but because of this, we often fall into the trap of making systems massively more complicated than they need to be.
Think about ownership.
Each object will either be owned by another, meaning that it should be created and destroyed as part of that object, or it will be referenced by another. You should design your interface accordingly. Ownership is a form of coupling, but it is often the correct design. It certainly simplifies your interface, as well as the burden on your callers to maintain their own objects.
Though shalt know and use thine collection libraries.
Know the collection libraries of whatever language / framework you are using, and use them. Also, try to make your own interfaces consistent with those libraries in terms of naming and behavior.
Concerning auditing.
You can do some auditing (logging, keeping statistics) from within your classes, but if your auditing needs are complex (needing to know when your data changes), it might be better to use an Observer pattern.