我正在尝试为消息传递队列(例如RabbitMQ)创建抽象,但是遇到了问题。
假设我有以下内容:
interface IMessagingQueue { }
interface IMessagingExchange
{
Bind(IMessagingQueue queue);
}
class RabbitQueue : IMessagingQueue { }
class RabbitExchange : IMessagingExchange
{
// FIX
Bind(IMessagingQueue queue) { }
}
class InMemoryQueue : IMessagingQueue { }
这很好用,因为客户端不依赖于特定的实现。示例:
class Client
{
public Client(IMessagingExchange exchange) { }
}
但是,在我标记为// FIX
的地方出现了问题。
在此示例中,InMemoryQueue
可以通过RabbitExchange
传递到Bind
,这在体系结构上没有意义。
当然,我可以介绍泛型la:
interface IMessagingExchange<TQueue> where TQueue : IMessagingQueue
{
Bind(TQueue queue);
}
但是,由于引入了泛型,现在客户端代码已绑定到实现:
class RabbitExchange : IMessagingExchange<RabbitQueue> { }
class Worker
{
public Worker(RabbitExchange exchange) { }
}
这确实引入了编译时安全性,但是却牺牲了交换实现的能力。
我是否想得太多?有办法解决这个问题吗?
答案 0 :(得分:1)
您希望能够无缝交换实现,但是不能只接受任何实现,因此这是我过去所做的事情,并且效果很好:
创建两个版本的接口,以便无需指定类型即可无缝交换:
interface IMessagingExchange
{
void Bind(IMessagingQueue queue);
}
interface IMessagingExchange<TQueue> : IMessagingExchange where TQueue : IMessagingQueue
{
void Bind(TQueue queue);
}
创建一个接口,以标识可与特定交换机一起使用的队列的子集。在这种情况下,我们确定Rabbit Exchange可以使用的队列:
interface IRabbitQueue : IMessagingQueue { }
RabbitQueue
类现在实现了以下接口:
class RabbitQueue : IRabbitQueue { }
我们添加了一个基类来处理类型检查:
abstract class ExchangeBase<TQueue> : IMessagingExchange<TQueue> where TQueue : class, IMessagingQueue
{
public abstract void Bind(TQueue queue);
public void Bind(IMessagingQueue queue)
{
var typedQueue = queue as TQueue;
if (typedQueue == null)
throw new InvalidOperationException($"This exchange only supports queues that implement {typeof(TQueue).FullName}");
Bind(typedQueue);
}
}
请注意,基类将实现未类型化的版本,为您进行类型检查,并将请求转发至具体类提供的类型化版本。
RabbitExchange
类现在从基类继承并提供绑定逻辑:
class RabbitExchange : ExchangeBase<IRabbitQueue>
{
public override void Bind(IRabbitQueue queue)
{
}
}
执行:
//Using untyped versions
IMessagingExchange exchange = new RabbitExchange();
IMessagingQueue queue = new RabbitQueue();
//This works fine
exchange.Bind(queue);
//Attempt to use the wrong queue
IMessagingQueue memoryQueue = new InMemoryQueue();
//This results in an error
exchange.Bind(memoryQueue);
此交换仅支持实现以下内容的队列 SomeNameSpace.IRabbitQueue
//We use a typed exchange this time
var rabbitExchange = exchange as RabbitExchange;
//This works
rabbitExchange.Bind(queue);
//This is still allowed because of the untyped interface, but causes an error because type is still checked
rabbitExchange.Bind(memoryQueue);
//Use a typed queue this time
var rabbitQueue = queue as RabbitQueue;
//This skips the base class validation because it calls the typed method in the concrete class
rabbitExchange.Bind(rabbitQueue);