【问题标题】:How to model this scenario using OOP? (inheritance issue)如何使用 OOP 对这个场景进行建模? (继承问题)
【发布时间】:2012-10-15 09:04:18
【问题描述】:

我有很多不同的引擎来实现不同的算法。它们都实现了相同的接口,但具有不同的配置方法。大部分都是不带参数配置的,有的只带一个整数,少的带两个整数。有小概率将来我们会有三个甚至四个整数。

我需要创建一个引擎控制器来决定何时必须启动或停止引擎,因为这对所有引擎来说都很常见。我认为的选项如下:

  1. 创建一个独特的接口,其参数与可用的最大配置方法一样多,并忽略引擎中不需要的参数。这样我就只有一个 EngineController。
  2. 为每个不同的配置方法创建一个接口,并为每个不同的接口创建一个 EngineController(但这会让我创建很多只在参数数量上有所不同的类,并且每个类都需要 2 个新类将新参数添加到引擎的时间。
  3. ...

我真的对这两种解决方案中的任何一种都不满意,因为传递不需要的参数看起来很“丑陋”,并且由于使用第二个选项生成的类数量众多(只有很小的差异)。

有什么设计或模式可以避免这个问题?

编辑(感谢您的回答,此编辑回答了所有问题并澄清了问题):

举个例子,这些是引擎。

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。控制器会在需要时调用 Configure、Start 和 Stop:

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 或其他)来传递未定义数量的参数。这个想法不错,但是我会丢失参数名称。但也许这是迄今为止最好的选择。

【问题讨论】:

  • 代码示例好吗?一个简化的,我只是想看看你的接口和类
  • 选项 1 听起来很糟糕,使用和维护会令人困惑。
  • 我也有一个question

标签: c# .net oop inheritance interface


【解决方案1】:

这对你有帮助吗?

    interface IEngine
    {
        void startEngine(params int[] engineParam);
    }

【讨论】:

  • 是的,这是一个新想法,但是……这样我就没有参数名称了。我会将其添加为第三个选项
【解决方案2】:

也许我不完全理解,但我认为你想要这样的东西:

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
  • IEngine 具有 Start() 方法。
  • Configuration 是一个 IConfigparameters 列表,具有 bool 可以启动(如果所有参数都可以)。
  • 每个参数都有一个根据某些条件计算的 IsOkToStart 也许这为您提供了灵活性?组合您需要的参数,并可能在将来添加 ned 参数。我相信界面非常小且具有凝聚力是一件好事。甚至可以将它们拆分为 IStartParameter 和 IstopParameter 并组合成所需的配置?

【讨论】:

  • 你能看看我的新例子,也许它可以澄清一些事情。谢谢!
【解决方案3】:

我会这样建模:

 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 容器注册约定,添加额外引擎和配置的工作仅限于添加引擎类并在需要时扩展配置类。
【解决方案4】:

IOC 容器来调用您需要的引擎或您需要的一堆引擎并在运行时注入它们,您可以在调用容器时将它们与可选参数结合使用。我已经在 .NET FW 的许多属性中看到了可选参数的用法。或使用对象参数列表来获取所有输入,并且在调用时可以解析列表并决定它打算调用哪个引擎。它们都不会难以掌握和使用

【讨论】:

  • 我不确定 IoC 可以如何提供帮助。
猜你喜欢
  • 1970-01-01
  • 2018-12-03
  • 2023-03-06
  • 1970-01-01
  • 2015-04-20
  • 2011-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多