gpt4 book ai didi

c# - 子对象与父对象的通信模式

转载 作者:行者123 更新时间:2023-11-30 22:36:27 40 4
gpt4 key购买 nike

我实际上正在用 C# 开发多线程游戏服务器。我正在尝试将其创建为尽可能可单元测试。

到目前为止,我开始使用的模式类似于这样的东西:

Server 类被实例化:

  • 用于启动和关闭服务器

OnStart 服务器实例化 ConnnectionManager :

  • 包含 ConnectionPool 类的一个实例
  • 用于接受异步连接(通过 TCPListener.BeginAcceptSocket)
  • 当一个套接字被接受时,一个ClientConnection的实例被创建并添加到ConnectionsPool中的列表中

ConnectionsPool 类:

  • 负责向池中添加和删除事件连接,作为列表进行管理

ClientConnection 类:

  • 负责通过Socket.BeginReceive接收数据
  • 当接收到所有内容时,将创建一个SocketHandler 实例(主要是解析套接字内容并运行操作)

架构应如下所示:
服务器 -> ConnectionManager -> ConnectionsPool -> ClientConnection -> SocketHandler

问题:
当我在 SocketHandler 内部时,我如何影响另一个玩家? (例如,玩家 A 击中玩家 B:我需要在 ConnectionsPool 中获取玩家 B 的实例并更新他的 HP 属性)或者甚至更新服务器本身(比如调用 Server 类的关闭方法)

我假设有 3 个选择:

  • 将所有重要的类(Server + ConnectionsPool)转换成静态类 > 缺点:无法进行单元测试
  • 将所有重要类转换为单例类 > 缺点:难以测试
  • 在子构造函数中注入(inject)重要类的实例>缺点:由于传递了很多信息,代码可读性较差

这里的目标是使用最佳实践使所有单元都可测试,同时保持简单的方法。

我收到了向 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/

40 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com