如何添加不会破坏OOP原理的新构造函数?

时间:2015-11-22 14:59:59

标签: c# oop

我有一个包装类,可以使用返回IAsyncCursor

IEnumerable<TResult>

我的程序可以从两个地方获取项目:

IEnumerable<TResult> GetItems()
{
    List<TResult> fromA = fromA();
    IEnumerable<TResult> fromB = fromB();

    var result = fromA.Concat(fromB).ToList();
    return result;
}

但我不想将result存储在内存中,因为它太贵了。

我想将GetItems()的返回结果更改为返回ResultCollection,并且能够调用Dispose()

因此,我需要将fromA()fromB()方法的结果更改为ResultCollection,如下所示

ResultCollection<TResult> GetItems()
{
    ResultCollection<TResult> fromA = fromA(); // wrap List<TResult> to ResultCollection
    ResultCollection<TResult> fromB = fromB(); // return collection that I can dispose when I need

    return new ResultCollection<TResult>(fromA.Concat(fromB).ToList());
}

如何更改ResultCollection以存储List构造函数?我需要这个新类符合所有OOPSOLID原则。

我不需要像{/ 1>这样的ResultCollection内只有另一个构造函数

public ResultCollection(IList<TResult> list)
{
    _list = list;
}

2 个答案:

答案 0 :(得分:2)

/* * Shared object containing a map of the world and * methods for getting/setting coordinates */ class World extends Thread { public $map; public $sx; public $sy; public $organisms; // set all map coords to 0 public function __construct($sx, $sy, $p) { $map = array(); for( $i = 0; $i < $sx; $i++ ) { $map[$i] = array(); for( $j = 0; $j < $sy; $j++ ) { $map[$i][$j] = 0; } } $this->map = $map; $this->sx = $sx; $this->sy = $sy; // create and start organism threads $this->organisms = array(); for( $i = 0; $i < $p; $i++ ) { // this won't work because threaded objects created // within a thread scope that have no external references // are destroyed in the constructor $this->makeOrganism($i+1, $i+1, $i+1); } } // produces a new organism, adds to world population public function makeOrganism($x, $y, $value) { if( $x < 1 || $x > $this->sx ) return false; if( $y < 1 || $y > $this->sy ) return false; if( $this->getMap($x, $y) != 0 ) return false; echo "creating new organism $value\n"; $organisms = $this->organisms; // doesn't work because the world data is decoupled in the new thread $organisms[] = new Organism($this, $x, $y, $value); $this->organisms = $organisms; return true; } // assumes valid coords public function setMap($x, $y, $value) { return $this->map[$x-1][$y-1] = $value; } // assumes valid coords public function getMap($x, $y) { return $this->map[$x-1][$y-1]; } public function getSX() { return $this->sx; } public function getSY() { return $this->sy; } public function run() { for( $i = 0; $i < count($this->organisms); $i++ ) { echo "starting organism ", $this->value, "\n"; $this->organisms[$i]->start(); } } } /* * Autonomously running agent accessing shared World map */ class Organism extends Thread { public $value; public $world; public $x; public $y; public function __construct(World $world, $x, $y, $value) { $this->world = $world; $this->value = $value; $this->x = $x; $this->y = $y; // assume coordinates are empty $this->world->setMap($x, $y, $value); } // try to move organism by $dx, $dy public function move($dx, $dy) { $x = $this->x + $dx; $y = $this->y + $dy; if( $x < 1 || $x > $this->world->getSX() ) return false; if( $y < 1 || $y > $this->world->getSY() ) return false; if( $this->world->getMap($x, $y) != 0 ) return false; $this->world->setMap($x, $y, $this->value); $this->world->setMap($this->x, $this->y, 0); $this->x = $x; $this->y = $y; return true; } public function getValue() { return $this->value; } public function run() { // infinite loop; organisms move randomly about until they die while( true ) { echo "running organism ", $this->getValue(), "\n"; // this should operate on the shared object World, // maintaining consistency and avoiding conflicts between threads $dx = rand(-1, 1); $dy = rand(-1, 1); $this->move($dx, $dy); // occasionally add an organism to the population by cloning this one if( rand(0, 100) > 95 ) { $this->world->makeOrganism($this->x+1, $this->y+1, $this->value+100); } // wait random interval, organisms are // not expected to move all at the same time $this->wait(1000 + rand(500, 1500)); } } } // initialize shared object $world = new World(50, 50, 50); $world->start(); $world->join(); 不应该返回GetItems。它应该返回DeferredResultCollection。然后你可以做到

IEnumerable<...>

这将是完全懒惰的。

整个班级return fromA.Concat(fromB); 永远不应该暴露给此API的消费者,因为它没有有用的公共成员。 DeferredResultCollection已经足够了。

事实上,根本不需要课程IEnumerable<...>。你可以用迭代器替换它。

这是一个充实的版本:

  1. 删除DeferredResultCollection
  2. 使用此:
  3. DeferredResultCollection

    这总是处理资源,即使在这些示例中:

    public static IEnumerable<TResult> CreateDeferredIEnumerable(IAsyncCursor<TResult> _asyncCursor)
    {
        if (_asyncCursor != null)
        {
            using (_asyncCursor) { //This is key
             for (; _asyncCursor.MoveNextAsync().Result;)
             {
                 foreach (var result in _asyncCursor.Current)
                 {
                     yield return result;
                 }
             }
            } //The Dispose is always triggered!
        }
    }
    

    为了支持concat,我们需要确保处置:

    1. ((IDisposable)CreateDeferredIEnumerable(...)).Dispose();
    1. ((IDisposable)CreateDeferredIEnumerable(...).GetEnumerator()).Dispose();
    2. CreateDeferredIEnumerable(...).ToList();
    3. CreateDeferredIEnumerable(...).Take(1).ToList();
    4. foreach (var x in CreateDeferredIEnumerable(...)) break;
    

    我希望这有效,它很快被黑客攻击。不过,它可以确保工作。

答案 1 :(得分:1)

您可以创建一个名为IDisposableEnumerable的新界面,该界面具有以下定义:

public interface IDisposableEnumerable<T> : IEnumerable<T>, IDisposable
{

}

然后让你的DeferredResultCollection类实现这样的接口。

然后你应该创建另一个包含任何IEnumerable<T>的接口的实现。这样的类可以被称为EnumerableWrapper,它在Dispose方法中没有任何作用。

您还需要创建另一个可以使用Composite Pattern将两个(或更多个)IDisposableEnumerable个对象合并在一起的实现,这个类可以称为CompositeDisposableEnumerable

请注意,在调用非最佳的异步方法时,您的DeferredResultCollection类会阻塞。

您可能还想考虑使用Reactive Extensions for .NET,但这可能会导致设计更改。

<强>更新

以下是EnumerableWrapperCompositeDisposableEnumerable的样子:

public class EnumerableWrapper<T> : IDisposableEnumerable<T>
{
    private readonly IEnumerable<T> m_Enumerable;

    public EnumerableWrapper(IEnumerable<T> enumerable)
    {
        m_Enumerable = enumerable;
    }

    public void Dispose()
    {

    }

    public IEnumerator<T> GetEnumerator()
    {
        return m_Enumerable.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable) m_Enumerable).GetEnumerator();
    }
}

public class CompositeDisposableEnumerable<T> : IDisposableEnumerable<T>
{
    private readonly IDisposableEnumerable<T>[] m_DisposableEnumerables;

    public CompositeDisposableEnumerable(params IDisposableEnumerable<T>[] disposable_enumerables)
    {
        m_DisposableEnumerables = disposable_enumerables;
    }

    public IEnumerator<T> GetEnumerator()
    {
        foreach (var disposable_enumerable in m_DisposableEnumerables)
        {
            foreach (var item in disposable_enumerable)
                yield return item;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Dispose()
    {
        foreach (var disposable_enumerable in m_DisposableEnumerables)
            disposable_enumerable.Dispose();
    }
}

<强>更新

以下是一个示例用法:

public IDisposableEnumerable<T> GetItems<T>()
{
    List<T> collection1 = ....;

    DeferredResultCollection<T> collection2 = new DeferredResultCollection<T> (async_cursor);

    return new CompositeDisposableEnumerable<T>(new EnumerableWrapper<T>(collection1), collection2);

}

从消费者方面来说:

using(var items = GetItems<string>())
{
    //do something with items
} //This will invoke `Dispose` which will be propagated finally to DeferredResultCollection.Dispose and thus to IAsyncCursor.Dispose