Вопрос о дизайне - ОО приложение еды - PullRequest
3 голосов
/ 18 февраля 2010

Скажем, у меня есть несколько пользовательских контролов, каждый пользовательский контроль внутри табе, внутри окна.

Например, допустим, это приложение для сбора продуктов питания. Тогда у нас есть вкладки Фрукты, Овощи и Закуски. На каждой вкладке будет показан список продуктов питания по этому предмету, и пользователь сможет добавлять, удалять, изменять продукты в каждом разделе. Еда хранится в отдельных текстовых файлах, т.е. Fruit.txt, Vegetable.txt, Snack.txt

Фактические текстовые файлы могут выглядеть примерно так (vegetable.txt):

Name        Carbs    Fat
Eggplant    2        1.1
Cucumber    3        0.5
etc

Теперь это большой список, и есть метод загрузки, который вытягивает все овощи в список

Вопрос, который у меня возникает, заключается в том, что этот метод loadVegetables находится в коде файла, и я заканчиваю тем, что повторяю этот метод загрузки повсюду, потому что у меня есть другой экран, такой как ReviewAllFood, AddVegetable и т. Д., Наряду со всеми методы загрузки для фруктов и закусок.

Это больше вопрос дизайна, мне интересно, как я настроил это, чтобы не повторять этот код. Я мог бы иметь класс VegetableManager (или что-то еще), где есть метод загрузки, но означает ли это на самом деле менее повторяющийся код? Затем на каждом экране я должен создать объект VegetableManager и в любом случае вызвать его метод загрузки. Поэтому я думаю, что эффективность не лучше, но я добился лучшего дизайна.

Я думаю, что мне здесь чего-то не хватает. Прошло много времени с тех пор, как я изучал сплоченность и сцепление, и я думаю, что сейчас я путаюсь с этими концепциями. Цените, если кто-то может предложить дизайн для этой ситуации и объяснить, почему он выбрал его и почему это лучше, чем то, как я делаю это в данный момент.

Спасибо за чтение.

Ответы [ 4 ]

3 голосов
/ 18 февраля 2010

Я мог бы иметь класс 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
    }
  }
}

Поскольку _veggies равен static, в памяти имеется только одна коллекция овощей, несмотря на тот факт, что несколько абонентов будут создавать экземпляр VegetableManager.Но поскольку он статический, если у вас есть многопоточное приложение (например, веб-сайт), вы должны синхронизировать доступ к этому полю во всех потоках (отсюда lock s).

Это верхушка айсбергас точки зрения хорошей объектной ориентации.Я рекомендую просмотреть SOLID принципы UncleBob и Domain-Driven Design ( бесплатная электронная книга ).

Итак, да, вычто-то повторяете, но все, что вы повторяете, - это вызов метода , и это нормально для повторения.DRY означает смягчение дублирования «логического» кода, то есть принятия решений и алгоритмов;простые вызовы методов не подпадают под это.Однако, если вы хотите, вы можете объединить логику в базовый класс, сделав это, эффективно изолируя пользовательские элементы управления от необходимости знать о VegetableManager, хотя я думаю, что это излишняя ориентация на объект или ООО: -)

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.

2 голосов
/ 18 февраля 2010

Я бы предложил вытащить овощи (или то, что вы загружаете) один раз, когда вы читаете файл.Затем вы сохраняете их в некоторой базовой модели данных.Вы можете связать список и любые другие необходимые элементы управления с базовой моделью данных.Данные загружаются один раз, но их могут отображать различные виды.

РЕДАКТИРОВАТЬ: Добавление кода

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
}
0 голосов
/ 18 февраля 2010

Я бы использовал шаблон хранилища для этого. Для начала создайте один класс, содержащий методы для извлечения объектов из каждого текстового файла:

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

Этот класс должен быть единственным в вашем приложении, который знает, что продукты на самом деле хранятся в текстовых файлах.

Как только вы начнете работать, вы можете рассмотреть возможность кэширования часто используемых данных для повышения производительности.

0 голосов
/ 18 февраля 2010
    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
    }
    .
    .
    .

Есть несколько вещей, которые вы должны учитывать при использовании дизайна, как указано выше

и должен читать:

...