Дизайн класса C # для загрузки данных из файла - PullRequest
0 голосов
/ 15 февраля 2011

Каков правильный (или хороший) дизайн, если мне нужен класс, который загружает свои данные из файла (при этом сохраняя функции загрузки в другом классе)?

Это то, что у меня сейчас. Не могу не думать, что есть лучший способ структурировать все это. Часть, которая сбивает меня с толку, - это когда 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 / etc (как вы ищите в словаре слово, которое вы не можете произнести по буквам).

Обновление / Примечание
Оба ответа (пока) указывают на шаблон Factory. Значит ли это, что для каждого метода Load у меня должен быть отдельный класс? Похоже, излишним в моем конкретном случае. Не означает ли это также, что мой класс Stub должен знать / определять формат файла (чтобы он мог вызывать правильный фабричный класс) вместо того, чтобы просто позволить классу Primary беспокоиться об этом? Кажется, торгует соединением для инкапсуляции.

Определенно знаю, что я должен использовать свойства (на самом деле) и интерфейс (на самом деле), но хотел бы упростить его для вопроса. Не думал об аспекте инъекции, который является хорошим предложением.

Если кто-нибудь может обновить свой ответ, чтобы показать, как будут работать несколько функций загрузки (пожалуйста, сделайте их простыми), я, скорее всего, приму. Я также рассматриваю возможность перемещения функций загрузки обратно в класс Primary в качестве альтернативного решения.

Ответы [ 2 ]

2 голосов
/ 15 февраля 2011

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

Ваше решение не так уж и страшно, но я бы порекомендовал создать интерфейс ILoader и реализовать его с помощью Loader.Кроме того, вместо того, чтобы «обновлять» загрузчик внутри Primary, вы должны вставить ILoader в Primary, либо в конструкторе, либо с помощью свойства в Primary.Вы по-прежнему можете сохранить реализацию по умолчанию, которая доставляет новости конкретному загрузчику, если хотите.Вот пример:

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 для использования базы данных, веб-службы и т. Д.

Еще одно замечание: я не особенно люблю передавать ваш объект в Loader и позволять Loader изменять его.Я бы предпочел создать экземпляр Loader и попросить его создать объект с нуля.Я всегда испытываю некоторое неудобство, когда спрашиваю другие объекты об изменении состояния моего собственного материала.Эта реализация будет выглядеть как фабричный шаблон.

Еще один незначительный момент, я вижу, что вы используете открытые поля для ваших данных.Это большое нет-нет, вы должны вместо этого использовать C # properties .Свойства поддерживают инкапсуляцию и двоичную совместимость.

Другая стратегия доступа к данным, на которую вы можете обратить внимание: Active Record - плохой дизайн, но простой в понимании инструмент и хороший для небольших систем.

1 голос
/ 15 февраля 2011

Это выглядит как довольно простой пример фабричного паттерна. Вы не хотите, чтобы создаваемый объект знал о фабрике. Так что вырвите 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);
  }
}
...