IDisposable 的享元和工厂问题
我似乎在精神上陷入了享元模式的困境。
首先,假设我有一个一次性类型 DisposableFiddle
和一个工厂 FiddleFactory
:
public interface DisposableFiddle : IDisposable
{
// Implements IDisposable
}
public class FiddleFactory
{
public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s)
{
// returns a newly created fiddle.
}
}
那么,在我看来,FiddleFactory
的客户端就很清楚了工厂声称对所制作的小提琴没有所有权,并且客户有责任在使用完小提琴后将其处理掉。
然而,我们可以说,我想通过使用 Flyweight 模式在客户之间共享小提琴:
public class FiddleFactory
{
private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;
public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s)
{
// returns an existing fiddle if a corresponding s is found,
// or a newly created fiddle, after adding it to the dictionary,
// if no corresponding s is found.
}
}
然后我觉得在道德上有义务使工厂本身成为一次性的,因为它创建了小提琴并在它们的整个生命周期中保留对它们的引用。但这会给那些认为自己拥有小提琴并因此应该处置它们的客户带来问题。
问题实际上是我调用工厂 FiddleFactory
而不是 FiddlePool
,以及“创建”方法 CreateFiddle
而不是 获取小提琴
?像这样:
public class FiddlePool : IDisposable
{
private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;
public DisposableFiddle GetFiddle(SomethingThatDifferentiatesFiddles s)
{
// returns an existing fiddle if a corresponding s is found,
// or a newly created fiddle, after adding it to the dictionary,
// if no corresponding s is found.
}
// Implements IDisposable
}
然后客户端会更清楚地知道它不会拥有返回的小提琴,并且池有责任处置这些小提琴。
或者这只能通过文档来轻松解决?
有没有办法摆脱困境呢?甚至还有困境吗? :-)
I seem to be mentally stuck in a Flyweight pattern dilemma.
First, let's say I have a disposable type DisposableFiddle
and a factory FiddleFactory
:
public interface DisposableFiddle : IDisposable
{
// Implements IDisposable
}
public class FiddleFactory
{
public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s)
{
// returns a newly created fiddle.
}
}
Then, in my opinion, it's quite clear to the client of FiddleFactory
that the factory claims no ownership of the created fiddle and that it's the client's responsibility to dispose the fiddle when done with it.
However, let's instead say that I want to share fiddles between clients by using the Flyweight pattern:
public class FiddleFactory
{
private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;
public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s)
{
// returns an existing fiddle if a corresponding s is found,
// or a newly created fiddle, after adding it to the dictionary,
// if no corresponding s is found.
}
}
Then I feel morally obliged to make the factory itself disposable, since it creates the fiddles and keeps references to them during all of their lifetime. But that would cause problems to the clients that assumed they owned the fiddles and should therefore dispose them.
Is the problem actually that I call the factory FiddleFactory
instead of, say, FiddlePool
, and the "creation" method CreateFiddle
instead of GetFiddle
? Like this:
public class FiddlePool : IDisposable
{
private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;
public DisposableFiddle GetFiddle(SomethingThatDifferentiatesFiddles s)
{
// returns an existing fiddle if a corresponding s is found,
// or a newly created fiddle, after adding it to the dictionary,
// if no corresponding s is found.
}
// Implements IDisposable
}
Then it's clearer to the client that it'll not own the returned fiddle and it's the pool's responsibility to dispose the fiddles.
Or can this only be readily solved documentation-wise?
Is there a way out of the dilemma? Is there even a dilemma? :-)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我可以看到解决这个问题的两种方法:
ThreadPool-style:重新设计类,以便
FiddlePool
提供一个接口来完成繁琐的事情。该池不会分发Fiddle
实例,因为它有一个FiddlePool.PlayFiddle
方法。由于池控制 fiddle 生命周期,因此它负责处理它们。SqlConnection-style:修改
Fiddle
的公共dispose方法,以便它实际上只是将fiddle返回到fiddle池(fiddle类封装的)。在内部,fiddle 池负责真正释放可处置资源。I can see two ways out of this problem:
ThreadPool-style: redesign the classes so the
FiddlePool
provides an interface to do fiddly things. The pool doesn't hand outFiddle
instances because it has aFiddlePool.PlayFiddle
method instead. Since the pool controls fiddle lifetimes, it's responsible for disposing of them.SqlConnection-style: modify
Fiddle
's public dispose method so that it really just returns fiddles to the fiddle pool (which the fiddle class encapsulates). Internally, the fiddle pool takes care of really releasing disposable resources.我同意你的第二个意见。术语“Pool”和“Get”确实让消费者更清楚。然而,它仍然没有让事情变得足够清晰,并且应该始终添加文档以确保完整、有效的理解。
I agree with your second opinion. The terminology 'Pool' and 'Get' do make things more clear to the consumer. However, it still does not make things clear enough, and documentation should always be added to ensure a full, valid understanding.
您应该做的不仅仅是文档和命名方法来告诉客户端不要调用 dispose。实际上,让客户端调用 dispose 会更好,这样就可以形成使用模式。从我们的数据库连接池的构建中得到一些指导。
数据库汇集了一堆连接,这些连接本身是池感知的。调用代码创建一个连接,打开它,然后调用关闭(处置)它。调用代码甚至不知道它是否被池化,这一切都由连接类在内部处理。对于池连接,如果连接已打开,则调用 Open() 将被忽略。 Close()/Dispose() 被调用,在池化连接的情况下,这实际上将连接返回到池中而不是关闭它。
您可以通过创建一个 PooledFiddle 类来执行相同的操作,该类重写 Dispose 并将对象返回到池中。理想情况下,客户甚至不必知道这是一个池化的 Fiddle。
You should do something more than just documentation and naming methods to tell clients not to call dispose. Really it would be better to have the clients call dispose so make that the usage pattern. Take some direction from our database connection pools are built.
The database pools a bunch of connections which themselves are pool-aware. Calling code creates a connection, opens it, and calls close (dispose) on it. The calling code doesn't even really know if it's pooled or not, it's all handled internally by the connection class. With a pooled connection, calling Open() is ignored if the connection is already open. Close()/Dispose() is called and in the case of a pooled connection this actually returns the connection back to the pool instead of closing it.
You can do the same thing by creating a PooledFiddle class which overrides Dispose and returns the object to the pool. Ideally the client wouldn't even have to know it's a pooled Fiddle.