- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我实际上正在用 C# 开发多线程游戏服务器。我正在尝试将其创建为尽可能可单元测试。
到目前为止,我开始使用的模式类似于这样的东西:
Server 类被实例化:
OnStart 服务器实例化 ConnnectionManager :
ConnectionsPool 类:
ClientConnection 类:
架构应如下所示:
服务器 -> ConnectionManager -> ConnectionsPool -> ClientConnection -> SocketHandler
问题:
当我在 SocketHandler 内部时,我如何影响另一个玩家? (例如,玩家 A 击中玩家 B:我需要在 ConnectionsPool 中获取玩家 B 的实例并更新他的 HP 属性)或者甚至更新服务器本身(比如调用 Server 类的关闭方法)
我假设有 3 个选择:
这里的目标是使用最佳实践使所有单元都可测试,同时保持简单的方法。
我收到了向 child 注入(inject)委托(delegate)的建议,这与第三种方法类似,但我不确定是否对此进行单元测试以及实际效果,因为当然一切都是多线程的。
这里最好的架构选择是什么?
最佳答案
我发现的主题是网络代码并不是真正可单元测试的,应该与您希望测试的代码(例如 Player 对象)隔离开来。在我看来,您似乎已将 Player 与 ClientConnection 紧密耦合,这可能会降低其可测试性。我还认为将 Player 与他的连接耦合可能违反了 SRP,因为他们有非常不同的责任。
我编写了一个系统,它看起来与您希望实现的目标相似,我通过仅通过委托(delegate)和接口(interface)将播放器链接到连接来完成此操作。我有一个单独的 World 和 Server 类库,还有第三个项目 Application,它引用了这两者,并创建了一个 Server 和 World 的实例。 My Player 对象在 World 中,Connection 在 Server 中——这两个项目不相互引用,因此您可以考虑没有任何联网条件的 Player。
我采用的方法是将SocketHandler抽象化,在Application工程中实现一个具体的handler。处理程序有一个对世界对象的引用,可以在创建时传递给它。 (我通过工厂类来做到这一点——例如,ClientConnectionFactory(在应用程序中),其中服务器只知道抽象连接和工厂。)
public class ClientConnectionFactory : IConnectionFactory
{
private readonly World world;
public ClientConnectionFactory(World world) {
this.world = world;
}
Connection IConnectionFactory.Create(Socket socket) {
return new ClientConnection(socket, world);
}
}
然后 ClientConnection 可以将 World 引用转发给它的处理程序,以及处理程序需要的有关 Connection 本身的任何信息,例如特别是 Send 方法。在这里,我只是将 Connection 对象本身传递给 Handler。
public ClientConnection(Socket socket, World world)
: base(socket) {
this.handler = new ClientHandler(this, world);
...
}
游戏逻辑和网络之间的大部分协调都包含在 ClientHandler 中,可能还有它引用的任何其他姐妹对象。
class ClientHandler : SocketHandler
{
private readonly Connection connection;
private readonly World world;
public ClientHandler(Connection connection, World world) {
this.connection = connection;
this.world = world;
...
}
//overriden method from SocketHandler
public override void HandleMessage(Byte[] data) {
...
}
}
其余的涉及 ClientHandler 创建它的 Player 对象,分配委托(delegate)或接口(interface)来更新操作,然后将玩家添加到 World 中的玩家池中。最初我使用 Player 中的事件列表来完成此操作,我使用 ClientHandler(知道 Connection)中的方法订阅了这些事件,但事实证明 Player 对象中有许多事件需要维护。
我选择的选项是在播放器的世界项目中使用抽象通知器。例如,对于运动,我会在 Player 中有一个 IMovementNotifier 对象。在 Application 中,我会创建一个 ClientNotifier,它实现了这个接口(interface)并向客户端发送相关的数据。 ClientHandler 将创建 ClientNotifier 并将其传递给它的 Player 对象。
class ClientNotifier : IMovementNotifier //, ..., + bunch of other notifiers
{
private readonly Connection connection;
public ClientHandler(Connection connection) {
this.connection = connection;
}
void IMovementNotifier.Move(Player player, Location destination) {
...
connection.Send(new MoveMessage(...));
}
}
可以更改 ClientHandler 构造函数以实例化此通知程序。
public ClientHandler(Connection connection, World world) {
this.connection = connection;
this.world = world;
...
var notifier = new ClientNotifier(this);
this.player = new Player(notifier);
this.world.Players.Add(player);
}
因此,最终的系统是 ClientHandler 负责所有传入的消息和事件,而 ClientNotifier 处理所有传出的内容。这两个类很难测试,因为它们包含很多其他废话。无论如何网络都不能真正进行单元测试,但是可以模拟这两个类中的 Connection 对象。 World 库是完全可单元测试的,无需考虑网络组件,这正是我想在设计中实现的目标。
这是一个很大的系统,我在这里没有介绍太多,但希望这能给你一些提示。问我是否需要有关它的更多具体信息。
如果我能提供更多建议,那就是避免任何静态或单例 - 它们会回来咬你。如果需要作为替代方案,则倾向于增加复杂性,但在需要的地方记录下来。我拥有的系统对于维护者来说可能看起来很复杂,但是有详细的记录。对于用户来说,它的使用特别简单。主要是本质上
var world = new World();
var connectionFactory = new ClientConnectionFactory(world);
var server = new Server(settings.LocalIP, settings.LocalPort, connectionFactory);
关于c# - 子对象与父对象的通信模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7016212/
我目前正在寻找一些关于 jQuery 的建议,因为我认为我做错了,即使我得到了我想要的结果。 我想在更改时将输入的值更改为最接近的具有 .milestone 类的输入的值。我想要更改的输入是保持输入,
我已经阅读有关绑定(bind)、调用、申请的文章近一周了,对我来说仍然很复杂。我想我写的这个 jsfiddle 需要它们。然而,我没能做到,因为我仍然很困惑。 我尽力写了一些我上周从遇到这个问题的开发
我有一个项目生成代码。生成时间真的很长,所以我把它分成了多个项目,每个项目产生了整体的 20%。原始 POM 成为“父 POM”,子项依赖于它,仅包含一个单独的 Artifact ID 和一两个更改的
我正在使用局部 View 来创建父 subview 。我最理想的是父 View 上的提交按钮,用于保存子值。 我有以下模型。 public class Course { public int
我刚刚开始学习Rust,并且在理解所有权如何在我的案例中遇到一些麻烦: use std::ops::IndexMut; // =====================================
我是 JavaScript 新手,想了解更多有关它实例化父/子对象的顺序的信息。更具体地说,我想从编译器/浏览器的 Angular 理解以下代码片段。 var parent = { child:
我正在测试 Azure IaaS,并遇到了一个非常基本的问题。我有一个父 VHD 和子 VHD,已使用 csupload 将其作为页面 blob 上传,并且门户中显示图像和磁盘。然后我尝试将 pare
我的应用程序会定期为我坚持使用的对象请求更新 Core Data到网络服务。然后我需要更新我在主要上下文中拥有的对象(默认情况下 AppDelegate 中提供的对象)。编辑对象的不是用户,所以我需要
texT text text text text text 如何直接获取来自.menu ? 里面的 child 不应该采取。
我一直需要影响与其他元素相关的元素,但我的方法有点业余! 即到 // matched item where script is called from LINK 我使用; $(thi
我有两个表: 父子“类别”: id name parent_id 1 Food NULL 2 Pizza 1 3 Pasta
Linux 上的 Python 2.7.6。 我正在使用从父级继承的测试类。父类保存了许多子类共有的许多字段,我需要调用父类的 setUp 方法来初始化这些字段。调用 ParentClass.setU
我有一个处理图像、相册和相册类别的数据库。 一个专辑可以有多个专辑(子专辑),并且只有 1 级深度。 一张专辑仅属于一个专辑类别。 在这里做了一些研究,我相信最合适的数据库模型是这个 album_ca
我有一个关键字表,其中每个关键字都分配有一个 ID,并且是唯一的。我有第二个表,将父关键字的 ID 链接到子关键字的 ID。一个关键字最多可以有大约 800 个 child 或根本没有。 child
我经常使用这个 CSS 选择器 parent>child。我的设计在 Mozilla 和 Opera 中看起来不错。 但在 IE 中,它很糟糕。我知道 > 在 IE 中无法识别,但在 IE 中有什么替
我一直在用一个父对象构建一个系统,它在其中创建各种子对象,每个子对象都需要一个主对象才能运行。现在,到目前为止,我一直在创建 shared_ptr和 Child* ,所以当 Parent 和 所有 C
我从以下两个类中收到序列化兼容性错误。只有父类CommericalCustomer 实现了序列化。当具有如下所示的父/子关系时,使用可序列化接口(interface)的正确方法是什么? public
我正在开发一个程序并学习父/子进程。目前我的子进程是 exit(variable); 在我的 main() 中我有: signal(SIGCHLD, chldHandler); 在我的 main()
考虑以下两个具体类: public class A { protected void foo() { System.out.println("A foo"); bar
所以,我正在尝试建立这样的父/子类关系: class ParentClass where C : ChildClass { public void AddChild(C child)
我是一名优秀的程序员,十分优秀!