使用长时间运行的构造函数设计对象

时间:2013-01-26 02:00:32

标签: c# .net design-patterns .net-3.5

我有一个类,旨在为我的整个应用程序中多次使用的某些特定文件提供快速访问某些元数据的功能。遗憾的是,某些元数据只能通过长时间运行的方法调用来提取。

我有另一个类为长时间运行的方法提供异步包装(可能是5分钟或更长时间,具体取决于文件的大小),但我正在试图弄清楚如何调用这个异步方法,如果它将它放入构造函数中是适当的,或者如果这种情况有更好的设计模式。

这里有一些伪代码试图说明我的问题:

public class MetaData
{
    public string Data1 { get; private set; }
    public string Data2 { get; private set; }

    public MetaData(String filepath)
    {
        var extractor = new ExtractMetaData(filepath);  //create instance of class that fetches the metadata

        this.Data1 = extractor.GetData1(); // short running method

        extractor.Data2Received += Data2Received;  
        extractor.GetData2Async();  // long running method, called with via async method

    }        

    private void Data2Received(object sender, MetaDataEventArgs args)
    {
        this.Data2 = args.Data;  // finally set Data2 property
    }
}

class ExtractMetaData
{

    public event Data2ReceivedEventHandler Data2Received;

    public ExtractMetaData (string filePath) { }

    public String GetData1();  // very fast method to get Data1
    public void GetData2Async();  // very slow method to get Data2

}

我想弄清楚的是,是否有更好的方法来实现这一目标?

现在使用我的代码,几乎没有等待构建MetaData,但是如果有人在MetaData.Data2方法返回之前尝试访问GetData2Async()属性并触发Data2Received事件,他们会得到null回复。但如果它们在返回后调用它将包含正确的信息。由于实际上没有办法通知用户此方法已经完成,我担心这会变成糟糕的用户体验,因为他们没有等待构造函数,但必须等待所有属性设定。

3 个答案:

答案 0 :(得分:2)

首先,您说没有办法通知用户已完成Data2。事实并非如此,您可以使用多种方式通知用户,例如事件或Task

但我认为你应该重组你的班级。你说获得Data2需要很长时间,这很可能意味着它会占用大量资源。因此,我认为除非必须,否则你甚至不应该尝试初始化Data2。你怎么知道的?用户必须告诉你。理想情况下,如果用户不想要Data2,他甚至不能访问它,这意味着将MetaData拆分为两个类:BasicMetaData和{{1 },继承自ExtendedMetaData

BasicMetaData中,您可以通过某种方式通知用户初始化已完成(最有可能使用事件),或者您可以让构造函数等到初始化完成(您可以使用{{ 1}}和ExtendedMetaData这样做。)

就个人而言,我认为最好的选择是如果你有一个返回Monitor.Wait()的静态工厂方法。这样,用户可以同步(使用Monitor.Pulse())或异步(使用Task<ExtendedMetaData>Result等待结果)。这在.Net 4.5(因为ContinueWith()),尤其是.Net 4.0中特别有用。很遗憾,您问题上的标记表明您使用的是.Net 3.5,它没有await。如果可能的话,我建议你升级。

答案 1 :(得分:1)

我认为您需要将注意力集中在以下模式上:延迟加载(仅在您真正需要时调用&#39; long&#39;方法)和Proxy(如果需要实现缓存层,隐藏内部实现) ,可能是对象底部有多种不同的图案)。如果您决定使用多个对象来确保整个功能 - 那么Facade也是合理的选择。

答案 2 :(得分:0)

您将获得此问题的几个不同答案。这是我的看法。

IMO,您不应该在构造函数中调用任何操作,例如您现在正在执行的操作。 <{1}}构造函数中的所有内容都不应该在那里开始。

当你实例化一个对象时,它可能抛出一个异常,这很好,但你的对象不会被构造。一些最佳实践。构造函数应该是短期运行的,并且应该确保在构造函数之后创建对象图。

还要看这个问题: How much code should one put in a constructor?

或者,您应该注入依赖项并创建填充数据的方法。

如果你能更好地描述你的问题,那会更有帮助。

您确实需要简化流程和设计。

相关问题