设计问题 - OO食品应用

时间:2010-02-17 23:58:29

标签: c# wpf oop coupling

假设我在窗口内有一些用户控件,每个用户控件都在一个tabitem中。

例如,假设这是一个食物收集应用程序。然后我们有水果,蔬菜和零食。每个选项卡将显示该主题的食物列表,并允许用户添加,删除,修改每个部分中的食物。食物存储在单独的文本文件中,即Fruit.txt,Vegetable.txt,Snack.txt

实际的文本文件可能看起来像这样(vegetable.txt):

Name        Carbs    Fat
Eggplant    2        1.1
Cucumber    3        0.5
etc

现在这是一个很大的列表,并且有一个加载方法可以将所有蔬菜拉入List

我的问题是这个loadVegetables方法在代码隐藏文件中,我最终在整个地方重复这个加载方法,因为我还有其他的其他屏幕,如ReviewAllFood,AddVegetable等,以及所有其他加载水果和零食的方法。

这更像是一个设计问题,我想知道我是如何设置的,不重复这段代码。我可以有一个加载方法所在的VegetableManager(或其他)类,但这实际上意味着重复的代码更少吗?然后在每个屏幕中我必须创建VegetableManager的对象并且无论如何都要调用它的加载方法。所以我认为效率明智并不好,但我确实做到了更好的设计。

我想我在这里遗漏了一些东西。我研究了凝聚力和耦合已经有一段时间了,我想我现在对这些概念感到困惑。感谢是否有人可以为这种情况提出设计并解释他们为什么选择它以及为什么它比我现在这样做更好。

感谢阅读。

4 个答案:

答案 0 :(得分:3)

  

我可以有一个VegetableManager(或   东西)类里面的加载方法   是,但这实际上意味着更少   重复代码?然后在每个屏幕我   必须创造对象   VegetableManager并调用它的负载   方法无论如何。

这样做不是效率(即表现)。关键是要将将数据加载到单个隔离对象中的细节进行封装。例如,假设您的站点变得非常大,并且您决定将数据存储移动到数据库以实现可伸缩性和性能。在您描述的现有代码中,您必须遍历每个用户控件或页面并更改加载方法的逻辑。在最好的情况下,这是一种痛苦,在最坏的情况下,你会错过一些或错误地复制粘贴。如果将逻辑封装到专用对象中,其唯一的责任是知道如何从某处加载数据,那么您只需要进行一次更改。

用户控制的代码隐藏:

protected void Page_Load(object sender, EventArgs e) {
  var veggieManager = new VegetableManager();
  VeggieListControl.DataSource = veggieManager.GetAll();
  VeggieListControl.DataBind();
}

VegetableManager.cs:

public class VegetableManager {
  private static Collection<Vegetable> _veggies;
  private static object _veggieLock;

  public ReadOnlyCollection<Vegetable> GetAll() {
    if (_veggies == null) {
      lock(_veggieLock) { //synchronize access to shared data
        if (_veggies == null) { // double-checked lock
          // logic to load the data into _veggies
        }
      }
    }

    return new ReadOnlyCollection(_veggies);
  }

  public void Add(Vegetable veggie) {
    GetAll(); // call this to ensure that the data is loaded into _veggies
    lock(_veggieLock) { //synchronize access to shared data
      _veggies.Add(veggie);
      // logic to write out the updated list of _veggies to the file
    }
  }
}

因为_veggiesstatic,所以内存中只有一个蔬菜集合,尽管多个调用者将实例化VegetableManager。但是因为它是静态的,如果你有一个多线程应用程序(例如一个网站),你必须在所有线程上同步对该字段的访问(因此lock)。

这是良好的面向对象方面的冰山一角。我建议仔细阅读UncleBob's SOLID principlesDomain-Driven Designfree e-book)。

所以,是的,你正在重复一些事情,但你所重复的只是一个方法调用,这是可以重复的。 DRY意味着减轻“逻辑”代码的重复,即决策和算法;简单的方法调用不属于这个范畴。但是,如果你愿意,你可以将逻辑合并到一个基类中这样做,有效地隔离用户控件,不必知道关于VegetableManager,虽然我认为这是面向对象的过度杀伤,或者OOO: - )

public abstract class FoodUserControl : UserControl {
  protected List<Vegetable> GetVeggies() {
    return new VegetableManager().GetAll();
  }
}

然后您的实际控件将来自此而不是来自UserControl。

<强>更新

Eager-loading VegetableManager.cs:

public class VegetableManager {
  private static Collection<Vegetable> _veggies;
  private static object _veggieLock;

  static VegetableManager() {
    // logic to load veggies from file
  }

  public ReadOnlyCollection<Vegetable> GetAll() {
    return new ReadOnlyCollection(_veggies);
  }

  public void Add(Vegetable veggie) {
    lock(_veggieLock) { //synchronize access to shared data
      _veggies.Add(veggie);
      // logic to write out the updated list of _veggies to the file
    }
  }
}

请注意,此加载版本不必对构造函数中的加载代码进行双重检查锁定。另请注意,加载代码位于static构造函数中,因为此代码初始化static字段(否则,您将从每个构造的文件中将数据重新加载到同一个共享{{1 }}}。由于蔬菜是急切加载的,因此您无需加载GetAll或Add。

答案 1 :(得分:2)

我建议你在阅读文件时拉出蔬菜(或者你正在加载的任何东西)。然后将它们存储在一些基础数据模型中。您可以将列表以及您需要的任何其他控件绑定到基础数据模型。数据加载一次,但各种视图都可以显示它。

编辑:添加代码

List<T> loadObjects(File file, ILineConversionStrategy strategy) {
   // read eaqch line of the file
   // for each line
   T object = strategy.readLine(line);
   list.add(object);
   return listOfObjects;
}

编辑2:数据模型

class FoodModel {
   List<Vegetable> getVegetables();
   List<Fruit> getFruit();
   // etc
}

答案 2 :(得分:0)

    public interface IEatable {}

    class Vegitable : IEatable 
    { string Name { get; set; } }
    class Fruit : IEatable 
    { string Name { get; set; } }

    public interface IEatableManager
    {
        List<Vegitables> LoadEatables(string filePath);
    }
    public class VetabaleManager : IEatableManager
    {
        #region IEatableManagerMembers    
        public List<Vegitable> LoadVegs(string filePath)
        {
            throw new NotImplementedException();
        }    
        #endregion
    }
    .
    .
    .

使用上述设计需要考虑几件事情

并且必须阅读:

答案 3 :(得分:0)

我会使用repository pattern来做这件事。首先,创建一个包含方法的类,以从每个文本文件中检索对象:

public class FoodRepository
{
    public IList<Vegetable> GetVegetables() { ... }
    public IList<Fruit> GetFruit() { ... }
    // etc.
}

此类应该是应用程序中唯一知道食物实际存储在文本文件中的类。

一旦开始工作,您可能需要考虑caching frequently used data来提高性能。