设计模式实现这种类型的延迟加载?

时间:2013-11-01 20:13:29

标签: c# design-patterns lazy-loading lazy-evaluation

我有很多类实现这样的接口:

public interface IProcessor
{
    Foo Foo { get; set; }
    int Process();
}

我有一家生产这些产品的工厂,而且我不知道会提前生产多少产品。问题是,检索必要的Foo对象非常昂贵,并且在IProcessor调用Process之前,该属性实际上并未在public IProcessor CreateProcessor(int fooId) { Foo foo = this.Context.GetFoo(fooId); return new ImplementedProcessor { Foo = foo }; } 中使用。

所以如果我的工厂有这样的方法:

GetFoo

我每次都要打电话给public IProcessor CreateProcessor(int fooId) { this.CurrentlyTrackedFooIds.Add(fooId); return new ImplementedProcessor { Foo = what??? }; } 。我真正喜欢的是这样的:

ImplementedProcessor

我们的想法是,在Foo需要Process进行this.Context.GetAllFoosAtOnceCheaply(this.CurrentlyTrackedFooIds); 方法之前,它会排队等待,以便可以像以下那样大量检索:

IProcessor

是否有任何延迟加载模式可以帮助这样的事情?我可以完全控制整个项目,因此我不受现有IProcessorFactory或{{1}}接口的限制。我可以想办法做到这一点(可能是某种代表吗?),但是它们中没有一个看起来很干净,我想通过在项目中拥有一百万个不同的实现来避免这种事情失控。任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:4)

因此,有两种类型可以让我们的生活超级变得简单。第一个是Lazy。构造Lazy对象时,为其提供一个返回值的函数。然后,当你要求Lazy的值时,它会启动函数(如果它没有启动),如果它没有完成则等待函数,然后返回值。它从不调用该函数两次。如果永远不会要求Value,它甚至不会关闭该功能一次。

然后我们可以创建ConcurrentDictionaryLazy个对象,这样我们就可以确保获得我们拥有的任何现有Lazy个对象。请注意,它的GetOrAdd方法显然是原子的。我们可以确定字典不会用另一个字体覆盖现有的Lazy对象;它要么返回现有的,要么设置一个新的。这意味着我们永远不会在代表相同ID的两个不同Value对象上调用Lazy

毕竟,编码并不多:

private  ConcurrentDictionary<int, Lazy<IProcessor>> lookup =
        new ConcurrentDictionary<int, Lazy<IProcessor>>();
public IProcessor CreateProcessor(int fooId)
{
    var lazy = lookup.GetOrAdd(fooId, new Lazy<IProcessor>(Context.GetFoo));

    return new ImplementedProcessor { Foo = lazy.Value };
}

答案 1 :(得分:3)

从.NET 4.0开始,框架为此提供了Lazy<T>类:

public interface IProcessor {
    Foo Foo { get; }
    int Process();
}

internal class ImplementedProcessor {
    internal Lazy<Foo> LazyFoo {get;set;}
    public Foo Foo {
        get {
            return LazyFoo.Value;
        }
    }
    public int Process() {
        ...
    }
}

public IProcessor CreateProcessor(int fooId) {
    return new ImplementedProcessor {
        LazyFoo = () => this.Context.GetFoo(fooId)
    };
}

然而,这不会批量检索Foo - 该过程将逐个进行。只有在您的某些Process()项目永远不会调用ImplementedProcessor时,您才会节省一些时间。

为此,您需要在检索到第一个Foo时创建一个懒惰初始化的缓存,并保留其余Foo以获取缓存值。不过,Lazy<T>不适合这个。在调用IProcessor之前,请考虑向fooId添加方法以获取缓存所需的Process,并添加一个检索所有感兴趣的ID的类。