解决客户端和服务器之间的循环依赖

Solving a circular dependency between a client and server

我有一个用 C# 编写的客户端和服务器 class。两者都需要相互通信(将来多个客户端将与服务器通信)。但是我运行陷入了循环依赖。因为我使用 unity 进行依赖注入,所以我的程序冻结并崩溃。

我尝试使用客户端和服务器之间的接口作为消息提供者。但无论如何它都会崩溃,因为接口的实现引用了客户端和服务器,并且它们引用了接口。

这个项目很大,但我试着把它缩减:

   public class Client
{
    private Server server;

    public Client(Server server)
    {
        this.server=server;
    }

    public SendToServer(string message)
    {
        server.Receive(message);
    }

    public Receive(string message)
    {

    }
}

public class Server
{
    private Client client;

    public Server(Client client)
    {
        this.client=client;
    }

    public SendToClient(string message)
    {
        client.Receive(message);
    }

    public Receive(string message)
    {

    }
}

所有这些 运行 都在 WPF 的 UserControl 中。客户端被注入到视图的构造函数中。 Unity 然后将服务器注入客户端构造函数,这将触发服务器构造函数,服务器构造函数将再次触发客户端构造函数。

public partial class ClientView: UserControl
{
    public ClientView(Client client)
    {
        this.DataContext = client;
        InitializeComponent();
    }

}

乍一看这看起来很可疑,但如果您正在使用此组合,则需要以某种方式打破依赖循环。

一种方法是创建依赖项的抽象,并实现一个不依赖于另一个的多个实现。例如

public interface IServer { void Receive(string message); void SendToClient(string message); }
public interface IClient { void Receive(string message); void SendToServer(string message); }

public class Client : IClient
{
    // This will be set as SimpleServer-implementation
    private readonly IServer _server;

    public Client(IServer server) => _server = server;

    public void SendToServer(string message) => _server.Receive(message);

    public void Receive(string message) => Console.WriteLine($"Client received: {message}");
}

public class Server : IServer
{
    private readonly IClient _client;

    public Server(IClient client) => _client = client;

    public void SendToClient(string message) => _client.Receive(message);

    public void Receive(string message) => Console.WriteLine($"Server received: {message}");
}

public class SimpleServer : IServer
{
    public void SendToClient(string message) { }

    public void Receive(string message) => Console.WriteLine($"SimpleServer received: {message}");
}

void Main()
{
    var container = new UnityContainer();
    container.RegisterType<IServer, Server>();
    container.RegisterType<IClient, Client>(new InjectionConstructor(container.Resolve<SimpleServer>()));

    var server = container.Resolve<IServer>();
    var client = container.Resolve<IClient>();

    server.SendToClient("Foo");
    client.SendToServer("Bar");
}

另一种处理此问题的方法是在工厂的帮助下推迟一个依赖项的初始化。这样我们就可以在第一次使用时延迟加载依赖项。在下面的示例中,我使用 Func 委托作为工厂并将其包装在 Lazy.

public interface IServer { void Receive(string message); void SendToClient(string message); }
public interface IClient { void Receive(string message); void SendToServer(string message); }

public class Client : IClient
{
    private readonly IServer _server;

    public Client(IServer server) => _server = server;

    public void SendToServer(string message) => _server.Receive(message);

    public void Receive(string message) => Console.WriteLine($"Client received: {message}");
}

public class Server : IServer
{
    private readonly Lazy<IClient> _client;

    public Server(Func<IClient> clientFactory) 
        => _client = new Lazy<IClient>(() => clientFactory());

    public void SendToClient(string message) => _client.Value.Receive(message);

    public void Receive(string message) => Console.WriteLine($"Server received: {message}"); }
}

void Main()
{
    var container = new UnityContainer();
    container.RegisterType<IClient, Client>();
    container.RegisterType<IServer, Server>();
    var server = container.Resolve<IServer>();
    var client = container.Resolve<IClient>();
    server.SendToClient("Foo");
    client.SendToServer("Bar");
}