类设计,多态加载方法,依赖项注入

时间:2018-06-26 07:15:35

标签: c# class dependency-injection unity-container class-design

我是一名初学者程序员,但遇到一些类设计问题。我想在游戏类中使用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);
   }
}

P.S。我正在用C#编码。

3 个答案:

答案 0 :(得分:1)

this之后:

您可以使用

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")));

答案 1 :(得分:1)

可以肯定的一个选择是将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来做到这一点。另一方面,如果您希望切换到另一个框架(这使它稍微容易一点)(基于Opinion),则可以尝试使用Ninject。在这种情况下,您只需执行以下操作即可:

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

无论如何,我认为设计还不错。

答案 2 :(得分:0)

考虑您要做什么。您要加载游戏,因此需要一些有关要加载的游戏的信息,并且在加载时需要此信息。因此,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加载。