Дизайн класса, метод полиморфной нагрузки, внедрение зависимостей - PullRequest
0 голосов
/ 26 июня 2018

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

Допустим, у меня есть класс:

public class MyGame {
   public IGameData _data;
   public ISolver _solver;
   public ILoader _loader;


   public MyGame(ILoader loader, ISolver solver) {
      _loader = loader; 
      _solver = solver;
   }

   public Load() {
      /*
      The loader might need a param or not, 
      how can I make the method Dependency Injection friendly
      */
       _data = _loader.Load();        
   }
}

Проблема в том, что я хотел бы иметь классы FileLoader(), NetworkLoader() и т. Д.Каждый из них требует разных параметров.Одно из решений, которое я получил, - это передать параметр при создании конкретного Loader:

var fileLoader = new FileLoader('somegamestate.txt');
var someSolver = new SomeSolver();
var game = new MyGame(fileLoader, someSolver);

Но такой дизайн, похоже, не очень хорошо работает с инфраструктурой внедрения зависимостей C # Unity, поскольку связывать логически неправильноимя файла FileLoader в системе в целом.

Я хотя и использую фабрику, но это не удовлетворяет мою душу:)

У вас есть какие-то идеи по поводу дизайна и как ондолжно быть сделано?

Заводской пример:

public class MyGameFactory {
   public ISolver _solver;
   public MyGameFactory(ISolver solver){
       _solver = solver;
   }

   public MyGame MakeGame(ILoader loader) {
       return new MyGame(loader, _solver);
   }
}

PS Я кодирую в C #.

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Подумайте, что вы пытаетесь сделать. Вы хотите загрузить игру, поэтому вам нужна информация о том, какую игру загружать, и эта информация необходима при загрузке. Поэтому Load() нужен параметр, который несет эту информацию вместо конструктора ILoader реализации. Допустим, информация - это номер ячейки сохранения, тогда ваш класс будет выглядеть так:

public class MyGame {
   public MyGame(ILoader loader, ...) {
      _loader = loader;
   }

   public Load(int saveSlotNumber) {
       _data = _loader.Load(saveSlotNumber);        
   }
}

Теперь реализация ILoader может преобразовать saveSlotNumber во все, что подходит. Если у вас есть FileLoader, он может загружаться с {Application-Path}\savegames\{saveSlotNumber}.sav или, если у вас есть NetworkLoader, он может загружаться с \\game-server\savegames\{saveSlotNumber}.sav.

0 голосов
/ 26 июня 2018

Один из вариантов наверняка - поместить метод Load в интерфейс, а остальные реализовать в другие классы.Это позволит вам всегда вызывать его из класса Game, но в то же время каждый экземпляр будет предоставлять свою собственную реализацию.

Интерфейс будет таким простым:

public interface ILoader {
    void Load();
}

Здесь вы вводите две строки, которые могут понадобиться вашему загрузчику.

public class Loader1 : ILoader {
    public string prop1 { get; set; }
    public string prop2 { get; set; }

    public Loader1(string param1, string param2) {
        prop1 = param1;
        prop2 = param2;
    }

    public void Load() {
        // Use prop1 and prop2
    }
}

И в этом случае вы вводите какой-то другой объект.

public class Loader2 : ILoader {
    public SomeObject prop1 { get; set; }

    public Loader2(SomeObject param1) {
        prop1 = param1;
    }

    public void Load() {
        // Use prop1
    }
}

Прелесть этого в том, что каждый загрузчик получитсвои собственные параметры через конструктор, но игра всегда будет вызывать метод Load одинаково.Если вам нужна фабрика, то сделайте это, но вы всегда можете использовать общий инжектор зависимостей, например, Unity, как вы упомянули.

Внедрение параметров в конструкторы возможно с большинством контейнеров внедрения зависимостей.Точно так же, как @Thierry прокомментировал в своем ответе, вы можете сделать это с помощью Unity.Если, с другой стороны, вы предпочитаете перейти на другую платформу, которая немного упрощает (основанную на мнении), вы можете, например, попробовать Ninject.В этом случае вы просто выполните что-то вроде этого:

Bind<ILoader>().To<FileLoader>().WithConstructorArgument("file.txt");

В любом случае, я думаю, что дизайн более чем хорош.

0 голосов
/ 26 июня 2018

После это :

Вы можете использовать

container.RegisterType<ILoader , FileLoader>(
                new InjectionConstructor("somegamestate.txt") //Old way to pass value to constructor - not flexible. 
                );

или

container.Resolve<MyGame>(new DependencyOverride<ILoader >(new FileLoader("somegamestate.txt")));
...