当班级想要耦合时
我对两个曾经很好地分开的班级有一个问题,但现在他们想结合起来。
无需过多了解问题的具体细节,情况如下:
我曾经有一个包含 3 个空间位置顶点的 Triangle 类。
class Triangle
{
Vertex a,b,c ; // vertices a, b and c
} ;
程序中有许多三角形实例,因此每个实例都保留了自己的顶点副本。 getArea()
、getCentroid()
等成员函数都写在 Triangle
类中,并且由于每个 Triangle
实例具有顶点 a、b 和 c 的副本,发现面积或质心不依赖于其他类。理应如此!
然后,出于其他原因,我想转向顶点数组/索引缓冲区样式表示。这意味着所有顶点都存储在位于 Scene
对象中的单个数组中,并且每个 Triangle
仅保留对 Scene
中顶点的引用,而不是顶点本身的副本。首先,我尝试切换为指针:(
class Scene
{
std::vector<Vertex> masterVertexList ;
} ;
class Triangle
{
Vertex *a,*b,*c ; // vertices a, b and c are pointers
// into the Scene object's master vertex list
} ;
如果您想知道这样做的好处,我这样做的原因主要是与共享顶点的三角形有关。如果 *a 移动,则使用该顶点的所有三角形都会自动更新) 。
这将是一个非常好的解决方案!但它不能可靠地工作,因为 std::vector 使指针无效,并且我在类 Scene
中使用 std::vector 作为主顶点列表。
所以我必须使用整数:
class Triangle
{
int a,b,c ; // integer index values
// into the Scene object's master vertex list
} ;
但现在我遇到了这个新的耦合问题:为了找到自己的区域或质心,类 Triangle
需要访问 class Scene
,而之前它没有。看起来我已经检查了一些东西,但实际上并非如此。
万年历?
I am having an issue with 2 classes that were once nicely separated, but now they want to couple.
Without getting too much into the specifics of the problem, here it is:
I used to have a class Triangle that contained 3 space-position vertices.
class Triangle
{
Vertex a,b,c ; // vertices a, b and c
} ;
There were many Triangle instances in the program, so each had retained their own copy of their vertices. Member functions such as getArea()
, getCentroid()
etc were written in class Triangle
, and since each Triangle
instance had copies of Vertex a, b and c, finding the area or centroid had no dependence on other classes. As it should be!
Then I wanted to move to a vertex-array/index buffer style representation, for other reasons. This means all vertices are stored in a single array located in a Scene
object, and each Triangle
retains only REFERENCES to the vertices in Scene
, not copies of the vertices themselves. At first, I tried switching out for pointers:
class Scene
{
std::vector<Vertex> masterVertexList ;
} ;
class Triangle
{
Vertex *a,*b,*c ; // vertices a, b and c are pointers
// into the Scene object's master vertex list
} ;
(In case you're wondering about the benefits, I did it for reasons mostly to do with triangles that share vertices. If *a moves then all triangles that use that vertex are updated automatically).
This would have been a really good solution! But it didn't work reliably, because std::vector invalidates pointers, and I was using a std::vector for the master vertex list in class Scene
.
So I had to use integers:
class Triangle
{
int a,b,c ; // integer index values
// into the Scene object's master vertex list
} ;
But now I have this new coupling problem: to find its own area or centroid, class Triangle
needs access to class Scene
where before it did not. It seems like I've fsck`d something up, but not really.
WWYD?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
为什么不让
Scene
中的vector
也只存储指针呢?这样,
Triangle
就可以继续使用Vertex *
,而您所要做的就是确保在Scene
中删除指针。 s 析构函数。Why not just have the
vector
inScene
just store pointers too?That way,
Triangle
can continue to useVertex *
's and all you have to do is make sure that the pointers are deleted inScene
's destructor.您可以将向量传递给其构造函数中的三角形,以便它可以保留对向量的引用。那么它就不需要访问或了解场景。
You could pass the vector to the triangle in its constructor so it can keep a reference to the vector. Then it does not need to access or know about a Scene.
在我看来,你的三角形确实取决于你的场景(因为它的顶点都是该特定场景的成员),所以让对象这样做并不可耻。事实上,我可能会给三角形一个强制性的场景*成员。
It seems to me that your Triangle really does depend on your Scene (since its vertices are all members of that particular scene), so there is no shame in making the object do so. In fact, I would probably give the Triangle an obligatory Scene* member.
从非耦合到耦合的变化是您决定尽可能共享顶点的自然结果。以前,每个三角形“拥有”其顶点,并且场景(大概)拥有一堆或三角形。
允许三角形共享顶点会改变基本模型——当/如果一个顶点可能在两个或多个三角形之间共享时,没有一个三角形可以再拥有该顶点。虽然可能(例如,使用像shared_ptr这样的东西)有一个分布式的共享所有权方案,但你现在正在做的事情可能更简单:每个顶点仍然有一个所有者,但是所有者现在是场景而不是单个三角形。
由于三角形现在只是将某些顶点分组到“拥有”集合中的一种便捷方式,而不是拥有顶点本身,因此三角形和拥有其顶点的集合之间的耦合更紧密也就不足为奇了。如果您非常关心它,您仍然可以隐藏共享所有权,以至少保留以前松散耦合的外观。
总体思路相当简单:不是每个三角形都知道保存三角形顶点的场景,而是创建一个结合场景 ID 和顶点索引的顶点代理类,这样三角形就可以像操作顶点代理对象一样操作顶点代理对象。它以前会有顶点对象。您并没有完全消除更紧密的耦合,但是您将更紧密耦合的“知识”隔离到单个类,这仅负责维护松散耦合的外观。
这样做的明显缺点是顶点代理对象可能存储大量冗余数据。例如,任何特定三角形中的所有顶点代理都清楚地表示同一场景中的顶点。如果您为每个顶点代理显式存储场景 ID,则您将存储场景 ID 的三份副本,而不是之前的一份。有时这是值得的,但有时则不然。如果这是一个真正的问题,您可以尝试想出一种方法来避免显式存储场景 ID,但这可能会涉及一些与语言无关(甚至接近)的技巧。
The change from uncoupled to coupled is a natural result of your decision to share vertices where possible. Previously, each triangle "owned" its vertices, and the scene (presumably) owned a bunch or triangles.
Allowing the triangles to share vertices changes that fundamental model -- when/if a vertex might be shared between two or more triangles, no one triangle can own that vertex any more. Though it's possible (e.g., with something like a shared_ptr) to have a distributed, shared ownership scheme, what you're doing right now is probably more straightforward: each vertex still has a single owner, but the owner is now the scene instead of the individual triangle.
Since a triangle is now only a convenient way of grouping some vertices in the "owning" collection instead of owning the vertices itself, it's no surprise that there's tighter coupling between the triangle and the collection that owns its vertices. If you care a lot about it, you could still hide the shared ownership to retain at least the appearance of your previous looser coupling.
The general idea would be fairly simple: instead of each triangle knowing the scene that holds the triangle's vertices, you'd create a vertex proxy class that combines a scene ID and a vertex index, so the triangle can manipulate the vertex proxy object just like it previously would have the vertex object. You don't completely eliminate the tighter coupling, but you isolate "knowledge" of the tighter coupling to a single class, that's only responsible for maintaining the appearance of looser coupling.
The obvious shortcoming of this would be that the vertex proxy objects are likely to store a fair amount of redundant data. For example, all the vertex proxies in any particular triangle are clearly representing vertices in the same scene. If you store the scene ID explicitly for each vertex proxy, you're storing three copies of the scene ID instead of one you'd have had previously. Sometimes this is worthwhile -- others it's not. If it's a real problem, you could try to come up with a way of avoiding storing the scene ID explicitly, but that's probably going to involve some tricks that aren't (even close to) language agnostic.
如果您仅在顶点列表的末尾添加或删除,请改用
deque
。If you're only adding or removing to the ends of the vertex list, use a
deque
instead.我不认为这太糟糕了。 Triangle 失去了一些通用性,成为了 Scene 的外围类,但如果它不用作外部接口(并且与内部缓冲区的那种链接表明不是),那就只是自然的演变。
我的解决方案与你的类似,但加了更多的糖。
这样,您就不必重新组织内存中的内容,而改编旧代码只需将
()
添加到->a
和.a 等,这可以通过搜索和替换来完成,并且无论如何都改进了 OO 风格。
或者,取消构造函数和
private
并使其成为 POD。I don't think it's too bad.
Triangle
lost some generality and became a peripheral class ofScene
, but if it's not used as an external interface (and that sort of linkage to internal buffers suggests not), that's just a natural evolution.My solution would be similar to yours under the hood, but with more sugar.
This way, you don't have to reorganize things in memory, and adapting old code merely requires adding
()
to->a
and.a
, etc, which can be done by search-and-replace, and improves OO style anyway.Or, do away with the constructor and
private
and make it POD.假设您只有一个
Scene
,您可以将其设为单例对象并通过静态方法访问顶点列表。如果您有多个
Scene
对象,则每个Triangle
恰好属于一个Scene
- 并且它应该“知道”它属于哪个场景。因此,您应该使用Scene
引用初始化每个 Triangle,并将其存储为类成员。Assuming you have only one
Scene
, you can make it a singleton object and access the vertex list via static methods.If you have multiple
Scene
objects, then eachTriangle
belongs to exactly oneScene
- and it should "know" which scene it belongs to. Therefore, you should initialize each Triangle with aScene
reference, and store it as a class member.