我是一名初学者程序员,但遇到一些类设计问题。我想在游戏类中使用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#编码。
答案 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
加载。