用于从文件加载数据的C#类设计

时间:2011-02-14 21:09:59

标签: c# class-design

如果我需要一个从文件加载数据的类(同时将加载函数保存在不同的类中),那么正确(或好)的设计是什么?

这就是我现在所拥有的。不禁认为有一种更好的方法来构建它。绊倒我的部分是Loader在继续之前必须在Primary中调用方法的时候。

class Primary {
  public int x1, x2, x3;   //data that is read from file or calculated

  public void LoadPrimary {
    Loader L = new Loader();
    L.Load(this);   //pass self as parameter (this can't be the  best way though)
  }

  public void DoStuff() {
    x1++; x2--;
  }
}

class Loader {
  public void Load(Primary PrimToLoad) {
    PrimToLoad.x1 = 2; PrimToLoad.x2 = 4; 
    PrimToLoad.DoStuff();   //call a method in the calling class (better way for this?)
    PrimToLoad.x3 = 6;
  }
}

class Stub {
  public void SomeMethod() {
    Primary P = new Primary();
    P.LoadPrimary();
  }
}

在我的实际代码中,我使用Loader类来封装从各种源读取的几种不同格式(因此有多个Load函数),否则我只需要在Primary中包含该函数并完成它。有没有办法让Loader类返回Primary而不是void(现在它正在传递一个Param)。它似乎太“耦合”,以这种方式做好设计。

有关更好地完成此方案的任何建议吗?我认为它很常见但是对于类设计或术语不够了解,无法在google / SO /等上找到答案(如何在字典中搜索无法拼写的单词)。

更新/注释
答案(到目前为止)都指向工厂模式。这是否意味着对于每个Load方法,我必须有一个单独的类?在我的特定情况下似乎有点矫枉过正。这是否也意味着我的Stub类必须知道/决定文件的格式(因此它可以调用正确的工厂类)而不是让Primary类担心它?似乎是用于封装的交易耦合。

绝对知道我应该使用属性(实际上是)和接口(实际上是)但是想要简化问题。没想到注射方面这是一个很好的建议。

如果有人可以更新他们的答案以显示多个加载功能如何工作(请保持简单)我很可能会接受。我也在考虑将负载功能移回到Primary类中作为替代解决方案。

2 个答案:

答案 0 :(得分:2)

实际上有几十种模式可以完成这项任务,您选择的模式取决于系统的复杂性,灵活性和可扩展性。

您的解决方案并不可怕,但我建议您创建一个界面,ILoader,并使用Loader实现它。此外,您不应在主要内部“新建”Loader,而应将inject ILoader转换为Primary,无论是在构造函数中还是在Primary上使用属性。如果需要,您仍然可以保留一个默认实现,以启动具体的Loader。这是一个例子:

class Primary {
  public int x1, x2, x3;   //data that is read from file or calculated
  private ILoader _loader;

  public Primary(ILoader loader) {
    _loader = loader;
  }
  public Primary() {
    _loader = new Loader();
  }

  public void LoadPrimary {
    _loader.Load(this); 
  }

  public void DoStuff() {
    x1++; x2--;
  }
}

interface ILoader {
  void Load(Primary primToLoad);
}

class Loader : ILoader {
  public void Load(Primary PrimToLoad) {
    L.x1 = 2; L.x2 = 4; 
    L.DoStuff();   //call a method in the calling class (better way for this?)
    L.x3 = 6;
  }
}

class Stub {
  public void SomeMethod() {
    Primary P = new Primary(new Loader());
    P.LoadPrimary();
  }
}

此方法使您的依赖项对您的客户端显式化。它还允许您使用ILoader的模拟实现进行测试。此外,您可以轻松地将ILoader的实现更改为使用数据库,Web服务等。

另一个注意事项是,我并不是特别喜欢将你的对象传递给Loader并让Loader修改它。我宁愿实例化一个Loader并要求它从头开始构造一个对象。当要求其他物体改变我自己的东西状态时,我总会感到有些不舒服。此实现看起来像工厂模式。

还有一点,我看到你正在使用公共字段来存储数据。这是一个很大的禁忌,你应该使用C# properties代替。属性支持封装和二进制兼容性。

您可能希望查看的另一种数据访问策略是Active Record - 设计错误,但易于理解工具并且适用于小型系统。

答案 1 :(得分:1)

这看起来像Factory模式的一个非常简单的例子。但是,您不希望创建的对象知道工厂。撕掉LoadPrimary(),然后改为:

class Primary {
  public int x1, x2, x3;   //data that is read from file or calculated

  public void DoStuff() {
    x1++; x2--;
  }
}

public interface PrimaryFactory 
{
    Primary Load();
}

public class FileTypeAPrimaryFactory {

    FileTypeAPrimaryFactory(File f) 
    {
       ...
    }

    public void Load() {
        var item = new Primary();
        item.x1 = 2; PrimToLoad.x2 = 4; 
        item.DoStuff(); 
        item.x3 = 6;
    }
}

public class FileTypeBPrimaryFactory {

    FileTypeBPrimaryFactory(File f) 
    {
       ...
    }

    public void Load() {
        var item = new Primary();
        item.x1 = 2; PrimToLoad.x2 = 4; 
        item.DoStuff(); 
        item.x3 = 6;
    }
}

class Stub {
  public void SomeMethod() {
    PrimaryFactory factory = PrimaryFactory(<get file>);
    Primary P = factory.Load();
  }

  public PrimaryFactory(File file) 
  { 
       if (<check file format>) return new FileTypeAPrimaryFactory(file);
       return new FileTypeBPrimaryFactory(file);
  }
}