3

假设我有许多用户控件,每个用户控件在一个 tabitem 内,在一个窗口内。

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

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

Name        Carbs    Fat
Eggplant    2        1.1
Cucumber    3        0.5
etc

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

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

这更像是一个设计问题,我想知道如何设置它以不重复此代码。我可以在 load 方法所在的位置有一个 VegetableManager(或其他东西)类,但这实际上意味着更少的重复代码吗?然后在每个屏幕中,我必须创建 VegetarianManager 的对象并调用它的加载方法。所以我想效率方面并没有更好,但我确实实现了更好的设计。

我想我在这里遗漏了一些东西。自从我学习内聚和耦合已经有一段时间了,我想我现在对这些概念感到困惑。欣赏是否有人可以针对这种情况提出设计建议,并解释他们为什么选择它以及为什么它比我目前的做法更好。

谢谢阅读。

4

4 回答 4

3

我可以在 load 方法所在的位置有一个 VegetableManager(或其他东西)类,但这实际上意味着更少的重复代码吗?然后在每个屏幕中,我必须创建 VegetarianManager 的对象并调用它的加载方法。

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

用户控制的代码隐藏:

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

蔬菜管理器.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。但是因为它是静态的,如果你有一个多线程的应用程序(例如一个网站),你必须在所有线程之间同步对那个字段的访问(因此是locks)。

就良好的面向对象而言,这是冰山一角。我建议仔细阅读 UncleBob 的 SOLID 原则领域驱动设计免费电子书)。

所以,是的,你在重复一些东西,但你重复的只是一个方法调用,重复一遍是可以的。DRY意味着减少“逻辑”代码的重复,即决策和算法;简单的方法调用不属于此范围。但是,如果您愿意,您可以将逻辑整合到一个基类中,从而有效地将用户控件与必须了解蔬菜管理器隔离开来,尽管我认为这是面向对象的过度杀伤力,或者 OOO :-)

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

然后,您的实际控件将从此派生,而不是从 UserControl 派生。

更新

急切加载的 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字段(否则,您将在每次构造时将文件中的数据重新加载到相同的共享static字段中)。因为蔬菜是预先加载的,所以您不需要在 GetAll 或 Add 中加载。

于 2010-02-18T00:33:38.747 回答
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
}
于 2010-02-18T00:10:54.217 回答
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
    }
    .
    .
    .

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

并且必须阅读:

于 2010-02-18T00:33:17.680 回答
0

我会为此使用存储库模式。首先,创建一个包含从每个文本文件中检索对象的方法的类:

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

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

一旦您开始工作,您可能需要考虑缓存常用数据以提高性能。

于 2010-02-18T00:34:11.657 回答