【问题标题】:Is it good practice to blank out inherited functionality that will not be used?清除不会使用的继承功能是一种好习惯吗?
【发布时间】:2011-02-08 17:49:34
【问题描述】:

我想知道是否应该更改我的一个项目的软件架构。

我正在为双方(实际上是主机和设备)使用共享代码的项目开发软件。这很有帮助,因为共享数据,例如枚举可以存储在一个中心位置。

我正在使用我们所谓的“通道”在设备和主机之间传输数据。每个通道都必须在设备和主机端实现。我们有不同种类的通道,普通通道和特殊通道传输测量数据。

我当前的解决方案在抽象基类中有共享代码。从那里开始,代码在两侧之间拆分。事实证明,在某些情况下我们可以共享代码但我们不能共享它,我们必须在每一方都实现它。

DRY 原则(不要重复自己)说你不应该有两次代码。

我现在的想法是连接例如的功能。设备端和主机端的抽象测量通道在一个具有共享代码的抽象类中。这意味着,一旦我们为该通道的设备端或主机端创建了一个实际类,我们就必须隐藏另一端使用的功能。

这是可以接受的做法吗:

public abstract class ChannelAbstract
{
    protected void ChannelAbstractMethodUsedByDeviceSide()    {  }
    protected void ChannelAbstractMethodUsedByHostSide()      {  }
}

public abstract class MeasurementChannelAbstract : ChannelAbstract
{
    protected void MeasurementChannelAbstractMethodUsedByDeviceSide()   {  }
    protected void MeasurementChannelAbstractMethodUsedByHostSide()     {  }
}

public class DeviceMeasurementChannel : MeasurementChannelAbstract
{
    public new void MeasurementChannelAbstractMethodUsedByDeviceSide()
    {
        base.MeasurementChannelAbstractMethodUsedByDeviceSide();
    }

    public new void ChannelAbstractMethodUsedByDeviceSide()
    {
        base.ChannelAbstractMethodUsedByDeviceSide();
    }
}

public class HostMeasurementChannel : MeasurementChannelAbstract
{
    public new void MeasurementChannelAbstractMethodUsedByHostSide()
    {
        base.MeasurementChannelAbstractMethodUsedByHostSide();
    }

    public new void ChannelAbstractMethodUsedByHostSide()
    {
        base.ChannelAbstractMethodUsedByHostSide();
    }
}

现在,DeviceMeasurementChannel 仅使用来自MeasurementChannelAbstract 的设备端功能。通过声明 MeasurementChannelAbstract protected 的所有方法/成员,您必须使用 new 关键字才能从外部访问该功能。

这是可以接受的,还是在以后使用代码时可能会出现任何陷阱、警告等?

【问题讨论】:

  • 您在几个地方说过他们共享代码,然后在其他几个地方您必须单独实现一些东西?它们之间究竟共享了什么,从而阻止您将它们分开?
  • @Jon Cage:目前(在将 sw 架构更改为上面的示例之前)我有 DeviceMeasurementChannelAbstract 和 HostMeasurementChannelAbstract。一个用于设备端,一个用于主机端。这两个通道分别继承自 DeviceChannelAbstract 和 HostChannelAbstract。这两个继承自一个名为 ChannelAbstract 的共享抽象类。现在我有两个变量,所有测量通道都需要,但普通通道不需要。那就是问题所在。希望这能澄清它而不是让你感到困惑:S

标签: c# oop architecture


【解决方案1】:

在我看来,您似乎还没有确定哪些代码是共享的。将所有通用/共享的东西保留在 MeasurementChannelAbstract 中而不是使用双方调用的不同方法不是更有意义吗?我会认为那些应该在继承的类中?

【讨论】:

    【解决方案2】:

    你可以用继承解决这个问题,像这样:

    public abstract class MeasurementChannelAbstract
    {
        protected abstract void Method();
    }
    
    public class DeviceMeasurementChannel : MeasurementChannelAbstract
    {
        public void Method()
        {
            // Device side implementation here.
        }
    }
    
    public class HostMeasurementChannel : MeasurementChannelAbstract
    {
        public void Method()
        {
            // Host side implementation here.
        }
    }
    

    ...或者通过组合,使用策略模式,如下所示:

    public class MeasurementChannel
    {
        private MeasurementStrategyAbstract m_strategy;
    
        public MeasurementChannel(MeasurementStrategyAbstract strategy)
        {
            m_strategy = strategy;
        }
    
        protected void Method()
        {
            m_strategy.Measure();
        }
    }
    
    public abstract class MeasurementStrategyAbstract
    {
        protected abstract void Measure();
    }
    
    public class DeviceMeasurementStrategy : MeasurementStrategyAbstract
    {
        public void Measure()
        {
            // Device side implementation here.
        }
    }
    
    public class HostMeasurementStrategy : MeasurementStrategyAbstract
    {
        public void Measure()
        {
            // Host side implementation here.
        }
    }
    

    在我看来,您希望在两个标准/测量通道设备/主机通道之间划分继承层次结构。一种方法是使用多重继承 - 但 C# 不支持多重继承(接口除外),在大多数情况下,基于组合的设计会更简单。

    【讨论】:

    • @richj:我认为你说得对。我有标准和测量通道(测量通道扩展了测量通道的功能)。我还需要为设备端和主机端分别实现。正如您所说,C# 不进行多重继承。因此我问自己是否可以有一个单一的继承链(接受某些类将具有主机和设备端的功能)并在最后阶段分离到主机/设备通道(取消功能是不需要)。
    • @richj:您使用继承解决问题的示例对我不起作用,因为我在 MeasurementChannelAbstract 中具有仅由设备端或其他方式使用的方法。我知道我可以将它们移至 DeviceMeasurementChannelAbstract 和 HostMeasurementChannelAbstract,它们将从 MeasurementChannelAbstract 继承。问题是说例如需要为双方实现的 TemperatureChannel 必须继承自这两个类之一,并且不能继承自 TemperatureChannelAbstract(可以定义此通道的共享数据)。
    • 我想知道从接口而不是抽象类的起点来解决问题是否会有所帮助。接口将是:IChannel、ISidedChannel:IChannel、IMeasurementChannel:IChannel、ISidedMeasurementChannel:ISidedChannel、IMeasurementChannel。使用这个想法,您可以为每种类型变体提供一个具体实现,或者您可以为每种类型提供一个具体实现并使用策略(DeviceSideStrategy,HostSideStrategy,...)处理变体。
    【解决方案3】:

    在我看来,您有点混淆了继承和组合。当您必须在继承的功能没有完全重叠的情况下“清除”/抛出异常时,您的继承图缺少一些中间类。这通常是因为某些功能应该来自其他类的成员实例,而不是被继承。

    还要考虑实用性,将所有内容映射到完美的 OOP 不是目标,目标是一个可维护的工作程序而不会带来巨大的痛苦。

    【讨论】:

    • @Pasi Savolainen:目前我想不出另一种方法来确保代码不会在主机/设备端重复,而不是共享具有任何一方功能的抽象基类.在可维护性必须很容易的断言下,我将不得不采用我前面提到的不适合各自端(设备/主机)的“消隐”功能的方法。这样我至少可以确保设备/主机端的通道实现继承自同一个基类,共享信息。
    • 我接受了你的回答,因为我觉得它最接近我们在这里面临的情况。只是为了记录,我们将继续使用我们当前的方法,我们在早期为设备/主机端实现拆分类,以避免将双方的代码保持在同一个类中所带来的混淆。这不是一个容易的决定,但考虑到其他开发人员试图理解代码,我认为这是最好的决定。它不会映射到完美的 OOP,但它可以工作并且更清晰。
    【解决方案4】:

    就像 Rich 暗示的那样:在具体实现之一中只需要在 MeasurementChannelAbstract 中声明的成员之一,这非常清楚地表明您的接口定义错误,因为它有多个职责。这意味着您的代码的客户(和读者)很难理解抽象并看到各种具体实现之间的差异。

    这被称为"Single Responsibility Principle",它对于良好的 OO 设计非常重要。

    (有关良好 OO 设计的更多信息,我建议您了解所有 SOLID 原则)。

    【讨论】:

      【解决方案5】:

      因此我问自己是否可以 有一个单一的继承链 (接受某些课程将有 主机和设备的功能 边)并进行分离 最后阶段的主机/设备通道 (消隐功能 不需要)

      我觉得还可以。但是您可以通过对 SimpleChannel 和 MeasurementChannel 使用不同的接口来消除不需要的功能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-03
        • 2021-08-20
        • 1970-01-01
        • 2012-11-30
        • 2019-08-18
        • 1970-01-01
        • 2014-10-13
        相关资源
        最近更新 更多