我有许多实现不同算法的不同引擎。所有这些都实现了相同的接口,但具有不同的配置方法。它们中的大多数是没有参数的配置,其中一些具有一个整数,甚至具有两个整数。将来我们将使用三个甚至四个整数都有很小的概率。
我需要创建一个发动机控制器,该发动机控制器何时必须启动或停止发动机,因为所有引擎都很常见。我认为的选项如下:
- 创建一个独特的接口,其参数与可用的最大配置方法一样多,并忽略引擎上不需要的界面。这样,我将只有一个EngineController。
- 为每种不同的配置方法创建一个接口,并为每个不同接口创建一个EngineController(但这将使我创建很多类别的类,这些类别仅在参数上有所不同,每个类别都需要2个新类时间添加了一个新参数。
- ...
我真的对这两个解决方案中的任何一个都不满意,因为传递的不需要参数看起来"丑陋",并且由于第二种选项生成的类别数量很高(只有很小的差异)。
任何避免此问题的设计或图案?
编辑(感谢您的答案,此编辑回答所有内容并阐明问题):
举一个例子,这些是引擎。
abstract class EngineBase
{
public void Start() {...}
public void Stop() {...}
}
class EngineOne : EngineBase
{
public void Configure(int parameter1) {...};
}
class EngineTwo : EngineBase
{
public void Configure(int parameter1, int parameter2) {...};
}
class EngineThree : EngineBase
{
public void Configure(int parameter1, int parameter2, int parameter3) {...};
}
由于所有引擎都具有相同的逻辑来决定何时或结束,因此我想创建一个处理它们的新类,称为EngineController。控制器将在需要时调用配置,开始和停止:
class EngineController
{
EngineBase _engine; ??? or what?
void SuperviseEngine() { ... _engine.Configure(x,x,...) ... _engine.Start() ...
}
我的第一个想法是将下一个方法添加到Enginebase类中:
abstract class EngineBase
{
public void Start() {...}
public void Stop() {...}
public void Configure(int parameter1, int parameter2, int parameter3) {...}
}
class EngineController
{
EngineBase _engine;
void SuperviseEngine() { ... _engine.Configure(x,y,z) ... _engine.Start() ...
}
忽略了不需要的参数,但我不喜欢这个想法。然后,我考虑执行以下操作:
interface I1ParameterConfigurable
{
public void Configure(int parameter1) {...};
}
interface I2ParameterConfigurable
{
public void Configure(int parameter1, int parameter2) {...};
}
interface I3ParameterConfigurable
{
public void Configure(int parameter1, int parameter2, int parameter3) {...};
}
然后为每种引擎创建3个不同的控制器:
class EngineController1Parameter
{
EngineBase _engine;
I1ParameterConfigurable _configurableEngine = _engine as I1ParameterConfigurable;
void SuperviseEngine() { ... _configurableEngine .Configure(x) ... _engine.Start()
}
class EngineController2Parameter
{
EngineBase _engine;
I2ParameterConfigurable _configurableEngine = _engine as I2ParameterConfigurable;
void SuperviseEngine() { ... _configurableEngine .Configure(x, y) ... _engine.Start()
}
您明白了,但是我觉得这将创建很多界面/类时,也许有办法避免这种情况。
多亏了您的答案,我有第三个选项与第一个选项相似,但使用数组(或iEnumerable或其他)传递不确定的参数。这个想法还不错,但是我将失去参数名称。但这也许是到目前为止的最佳选择。
对您有帮助的意愿。
interface IEngine
{
void startEngine(params int[] engineParam);
}
也许我不完全理解,但我认为您想要这样的东西:
public interface IEngineController //I dont see a need to expose the enigine here in this pseudo code
{
void Start();
IConfiguration Config { get; }
}
public interface IEngine
{
void Start();
}
public interface IConfiguration
{
bool IsOkToStart { get; }
}
public class Configuration : IConfiguration
{
public Configuration(List<IConfigurationParameter> configurationParameters)
{
ConfigurationParameters = configurationParameters;
}
public bool IsOkToStart
{
get { return ConfigurationParameters.All(cfg=>cfg.IsOkToStart); }
}
protected List<IConfigurationParameter> ConfigurationParameters { get; private set; }
}
public interface IConfigurationParameter
{
bool IsOkToStart { get; }
}
public interface IMaxTemp : IConfigurationParameter
{
double MaxTemp { get; }
}
public interface ISafetyParameter : IConfigurationParameter
{
ISafetyCondition SafetyCondition { get; }
}
这有点长,我省略了stop()。这个想法是:
- 控制器具有IENGINE(未在接口中暴露)和ICONFIG
- IENENGINE具有start()方法。
- 配置是可以启动的IconFigParameters的列表(如果所有参数都可以)。
- 每个参数都有一个ISOKTOSTART,该ISOKTOSTART根据某种条件进行计算也许这为您提供了灵活性?结合所需的参数,并可能在将来添加NED参数。我相信界面非常小且具有凝聚力是一件好事。也许甚至将它们分为iStartParameter和Istopparameter,然后将它们组合到所需的配置?
我会建模与此类似:
public interface IEngine1 {
}
public interface IEngine1Config {
int Param1 {get;}
}
public Engine1 : IEngine1 {
IEngine1Config _config;
public Engine1(IEngine1Config config) {
_config = config;
}
}
然后,您可以选择选择一个类实现不同的引擎配置:
class AllEnginesConfig : IEngine1Config, IEngine2Config {
int Param1 {get;set;}
// ... etc
}
(当然,在您的情况下也可以在单独的类中实现配置更好)
如果您有很多引擎,我将使用IOC容器注册所有不同类型,并让其连接所有依赖项。
container.Register<IEngine1, Engine1>();
var theOneAndOnlyConfig = new AllEnginesConfig() {}; // properly initialized, of course
container.RegisterInstance<IEngine1Config>(theOneAndOnlyConfig);
container.RegisterInstance<IEngine2Config>(theOneAndOnlyConfig);
// ...
然后,要实例化引擎,您只需使用容器:
container.Get<IEngine1>();
ioc容器以调用所需的引擎或您需要的一堆引擎并在运行时注入它们,并且可以在调用容器时将它们与可选参数结合使用。我已经看到了.NET FW的许多属性中可选参数的用法。或使用对象参数的列表获取所有输入,而当调用时,可以解析列表并确定打算调用哪种引擎。他们都不难以理解和使用