循环类引用不良习惯?
最近有人告诉我如何以1类形式循环引用调用2类调用1类被认为是非常糟糕的做法,我不能让我的头在附近。我的意思是,如果这些课程在两个不同的项目中,我完全理解这个问题,但如果他们在同一个项目中可能会很糟糕吗?在某些情况下......你究竟如何预防这种情况?循环类引用不良习惯?
例如:我有一些类型的服务器。客户端连接到它,并且来自套接字的客户端或持有它的客户端负责管理网络内容以及帐户ID等信息。当客户端有新内容时,客户端会调用分组处理程序,现在数据包处理程序需要来自客户端的信息,并且必须发回信息。我将客户端传递给数据包处理程序,因此它可以将其称为发送函数等。
关注此想法的人这是所提到的不良做法,并且尽量不要这样做,尽管我很少见到服务器,尤其是那些将所有数据包处理保存在客户端类中的服务器。此外,你可能比处理程序更进一步,并且调用更多的类。将所有内容都保存在客户端是一团糟。所以... 这个真的很不好练?
如果是这样,一个人会怎样呢?要完成这件事,你需要的对象或多或少复杂的集合中的客户端,你可以传下去,而不必再次调用客户端的功能...
就像我说的,我真的不能把我的头围绕着这个“问题”。有人可以帮我吗?
当人们谈论循环引用,他们通常讲的项目/ DLL引用。这些在解决引用问题上存在问题,并且Visual Studio不会让您添加循环引用。
但你指的并不是项目结构,而是建筑,这里的东西有点复杂。类互相呼叫并没有本质上的错误。事实上,这在.NET中的回调函数和事件的设计中是隐含的 - 当你注册一个事件时,你实际上正在调用一个类,稍后会用事件处理函数回调你。
然而,这种形式的通知,要求的相对分离。服务器没有对客户端的EXPLICIT引用,而只有被调用的订阅客户端列表。如果你没有这样做,而是例如数据包处理程序持有对客户端的明确引用,那么这两个类将紧密耦合 - 数据包处理程序依赖于客户端的特定实现,反之亦然。
为什么这样不好?在我看来,这违反了分离关注的原则,这是编程中最基本的概念之一。客户端应该知道如何处理客户端操作,数据包处理程序应该处理数据包操作,并且不应该知道另一个如何工作,并且只能通过定义明确的特定接口进行通信。
让我们看看基于OP的非常精简的假设情况,它有循环引用: 客户端调用包处理程序的方法Send()
。数据包处理程序现在开始连接,然后发现它需要用户名/密码。它调用客户端上的一个方法来获取它,然后将其发送到服务器,获取响应,然后回调客户端以返回它。
在这种情况下,数据包处理程序现在绑定到客户端的实现。它要求客户端有一个GetCredentials()方法和一个MessageReceived方法来回调它。现在想象一个更加分离的场景:
客户端首先注册处理程序的ResponseReceived事件。现在客户端调用数据包处理程序的Send()方法。数据包处理程序需要身份验证,因此失败 - 它会抛出异常或返回错误代码,说“无法连接”。客户端获得此响应,并再次调用,这次使用Send(用户名,密码)方法。它会成功,获得响应,并引发ResponseReceived事件,并将响应发送给任何订阅了它的人。
这使得数据包处理程序可以在其他客户端的其他上下文中重用。它允许对客户端或处理程序进行内部更改,而对其他组件的影响较小。它使更简单,更容易维护的代码。这很好。 :)
你可以尝试提取一个对B调用的功能和B关于A调用的那些,然后在类库C封装起来,使得其他库可以毫无问题调用。很显然,C一定不能有参考既不是A也不B.
确实有一些情况,其中一个圆形的类参考并不是很糟糕。总会有一个实例化问题(鸡或鸡蛋)。永远记住,现在已经有两种方法在对象之间进行通信。您可以通过调用一个函数来等待下一个消息,当有新消息时,可以让客户端返回。
但这是一个你应该停下来思考的地方。尤其是在下列情况下(有更多,但这些都是在我的头顶):
- 当你通知类的一些动作已经happend - >使它成为一个事件。
- 当它是一对一的关系- >应该将类型合并为一个。
- 当您的课程扩展其他- >的功能时,将继承其功能,而不是传递参考。
在你的情况客户端< - >数据包处理程序。我可能不会在那里保留循环引用。我可能会为这两个类编写一个控制器,它们充当它们之间信息的代理。我也不会将该帐户ID存储在客户端对象中。它对我喜欢遵守的单一责任原则稍微有些勉强。我可能会落得这样的:
- 客户端 - >插座(负责网络的东西)
- ClientHandler的(负责控制流量)
- 客户
- PacketHandler
- 附加信息
- PacketHandler
同一个包中的类应该被认为是高度内聚的(假设你的包结构是正确的!)并且可以有更紧密的耦合。在包装内,循环关系 - 如果需要的话 - 都可以,但可以从基于特定界面(如以前的海报中所述)的设计中受益。
跨越包边界,内聚力是自然下和耦合应保持尽可能低:绝对没有圆形关系。
F#使循环类引用真正有趣。 – ChaosPandion 2012-04-18 06:51:24
查看“相关”问题列表。 --------->看起来像有几个可以帮助回答这个问题。 – 2012-04-18 06:55:58
在大多数情况下,我发现我可以将这些代码重新组合到消费者/生产者问题中,从而消除了一半耦合,从而实现了整体设计更清洁。在这种情况下,我使用*特定的接口*(我尝试去做);再次,我发现这些可以最大限度地减少对核心要求方面的“循环引用”,并使程序更加清晰。 – 2012-04-18 07:08:35