问题 IDisposable的Flyweight和Factory问题


我似乎在精神上陷入了Flyweight模式的困境。

首先,假设我有一次性用品 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 代替 GetFiddle?喜欢这个:

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
}

然后对客户来说更清楚的是它不会拥有返回的小提琴,并且游泳池有责任处理小提琴。

或者这只能在文档方面轻松解决吗?

有没有办法摆脱困境?还有进退两难的局面吗? :-)


9661
2018-02-25 17:34


起源



答案:


我可以看到解决这个问题的两种方法:

  • 线程池式:重新设计类,所以 FiddlePool 提供了一个做繁琐事情的界面。游泳池没有分发 Fiddle 实例,因为它有一个 FiddlePool.PlayFiddle 方法而不是。由于池控制了生命周期,因此它负责处理它们。

  • SqlConnection的风格:修改 Fiddle公共处置方法,以便它真正只是将小提琴返回到小提琴池(小提琴类封装)。在内部,小提琴池照顾  释放一次性资源。


7
2018-02-25 17:43



谢谢,第一个遵循德米特定律更好,第二个更符合我的整体设计。嗯... - Johann Gerell
我将把我的两分钱用于第一种方法 - 我看到有太多人尝试编写自己的SqlConnnection池! (不是,但我 有 必须解释为什么没有必要。) - Jeff Sternal


答案:


我可以看到解决这个问题的两种方法:

  • 线程池式:重新设计类,所以 FiddlePool 提供了一个做繁琐事情的界面。游泳池没有分发 Fiddle 实例,因为它有一个 FiddlePool.PlayFiddle 方法而不是。由于池控制了生命周期,因此它负责处理它们。

  • SqlConnection的风格:修改 Fiddle公共处置方法,以便它真正只是将小提琴返回到小提琴池(小提琴类封装)。在内部,小提琴池照顾  释放一次性资源。


7
2018-02-25 17:43



谢谢,第一个遵循德米特定律更好,第二个更符合我的整体设计。嗯... - Johann Gerell
我将把我的两分钱用于第一种方法 - 我看到有太多人尝试编写自己的SqlConnnection池! (不是,但我 有 必须解释为什么没有必要。) - Jeff Sternal


我同意你的第二意见。术语“池”和“获取”确实使消费者更清楚。但是,它仍然不够清楚,应始终添加文档以确保完整,有效的理解。


2
2018-02-25 17:39



我的想法完全正确另外,我可能不会把你的类叫做DisposableFiddle,除非你想让用户Dispose()它... - Reed Copsey
@Reed:同意DisposableFiddle。正确的命名对于传达意图至关重要,并且是防止错误使用的第一道防线。 - jrista
嘿,是的,我只是将Disposable添加到示例名称中,以便更容易遵循我的(困惑)步骤... - Johann Gerell


您应该做的不仅仅是文档和命名方法,以告诉客户不要调用dispose。真的,最好让客户端调用dispose,以便制作使用模式。从我们的数据库连接池中建立一些方向。

数据库汇集了一堆本身可以识别池的连接。调用代码会创建一个连接,打开它,并在其上调用close(dispose)。调用代码甚至不知道它是否合并,它都是由连接类在内部处理的。使用池连接,如果连接已打开,则忽略调用Open()。调用Close()/ Dispose(),并且在池连接的情况下,这实际上将连接返回到池而不是关闭它。

您可以通过创建一个PooledFiddle类来执行相同的操作,该类重写Dispose并将对象返回到池中。理想情况下,客户甚至不必知道它是一个汇集的小提琴。


1
2018-02-25 17:49



“理想情况下,客户甚至不必知道它是一个混合的小提琴。” - 是的,我同意这一点。我认为这是一个隐藏得更好的实现细节。 - Johann Gerell