【问题标题】:How should i refactor this?我应该如何重构这个?
【发布时间】:2011-03-09 20:17:12
【问题描述】:

所以在我的应用程序中,我有几个不同的客户正在“服务”。每个客户都有自己的各种类的实现,这些类都基于接口。

添加最新客户后,我注意到其他客户的代码会重复很多,但其他客户与他们没有任何关系。

我已经为其他几个客户提供了默认实现,并根据需要推出新的客户。

我的问题是我如何重构这个并且仍然保持代码干净?如果我是这个代码库的新开发人员,我希望每个客户要么使用这些类的默认实现,要么使用他们自己的实现......但这有很多重复。

【问题讨论】:

  • 添加代码示例或更多详细信息确实会有所帮助;当前的问题听起来你已经远远超出了应该实现一些继承或接口链的地方

标签: c# .net oop refactoring interface


【解决方案1】:

考虑使用带有abstractvirtual 成员的abstract 基类。抽象成员本质上等同于接口成员(它们没有内置行为,它们只保证方法存在)而virtual 成员具有默认实现,可以被派生类覆盖。

您的问题实在是太模糊了,无法完整回答,但这里是您可以利用继承的方法。

如果您希望所有类使用成员的相同实现,则可以在基类中实现该成员。

如果您希望每个类都有自己的成员实现,那么您可以使用具有abstract 成员的基类或接口。

如果您希望一些类使用相同的实现,而其他类使用不同的实现,然后在基类中实现默认行为并根据需要覆盖它。


我的主要观点是,OOP 在基本/抽象/具体类中存在多少功能或多少功能。没有灵丹妙药的答案,有时您的基类将是骨架,有时它们会完全充实;这一切都取决于手头的具体问题。

【讨论】:

  • 虽然我原则上同意,但这可能变得比其他选项更困难(例如使用策略)如果客户没有形成清晰的层次结构.根据我的经验,在这种情况下聚合往往会更好。
  • 我同意,但也许你可以更清楚地说明策略和继承之间的区别——我一直以不同程度的继承实现策略模式(有时他们只是实现一个接口,有时纯粹是抽象的基类,有时主要是虚拟基类)
【解决方案2】:

您可以使用继承(将通用逻辑放在基类中)或聚合(将该逻辑传播到其他类并让您的客户使用它们)。

【讨论】:

    【解决方案3】:

    有什么方法可以创建一个基类,然后为每个客户创建一个特定的实现,然后使用某种类型的依赖注入来根据需要加载类或功能。您希望真正拥有一个 DRY 系统,以避免头痛和拼写错误或其他类似的人为错误。

    【讨论】:

    • 只是为了确保我理解你;您是在为所有具体客户类建议一个基类,对吧?每个具体类没有一个基类...
    • 是的,对不起,我措辞不正确。是的,我的意思是一个基类,每个客户都有特定的实现。然后将通用功能留给 DI 或其他模式。谢谢你抓住那个。我似乎错过了两者之间的一部分。有时您会想到一些东西但忘记输入它。
    【解决方案4】:

    如果没有更好地理解代码,很难知道该建议什么......但在类似情况下对我有用的一些事情包括:

    • 使用Strategy 表示重复的代码。在将策略封装在实现已知接口的类中(每个备用策略一个类)的情况下,我取得了最大的成功。通常在这种情况下,我使用某种形式的依赖注入框架(通常是 StructureMap)将适当的策略/策略传递给类。
    • 对公共项目使用某种模板类(或模板方法)。
    • 使用Decorator 为一些基本客户添加特定功能。

    STW 建议我应该澄清一下“策略”的含义以及它与正常继承有何不同。我想继承是您非常熟悉的东西 - 基类中的某些东西(通常是方法 - abstractvirtual)被派生类中的替代实现替换。

    策略(至少我通常使用它的方式)通常由完全不同的类实现。通常,该类所包含的只是单个可替换操作的实现。例如,如果“操作”是要执行一些验证,您可能有一个 NullValidationStrategy 什么都不做,一个 ParanoidValidationStrategy 确保每个 McGuffin 的高度、宽度和特定的蓝色阴影都正确。我通常在自己的类中实现每个策略的原因是因为我尝试遵循Single Responsibility Principle,这样可以更容易地在以后重用代码。

    正如我上面提到的,我通常使用依赖注入 (DI) 框架通过类构造函数“注入”适当的策略,但可以通过其他机制获得类似的结果 - 例如具有SetSomeOperationStrategy(ISomeOperation StrategyToUse) 方法或保存策略引用的属性。如果您不使用 DI,并且对于给定的客户类型,策略将始终相同,那么您始终可以在构建类时设置正确的选择。如果给定客户类型的每个实例的策略都不相同,那么您可能需要某种客户factory(通常factory method 就足够了)。

    【讨论】:

      【解决方案5】:

      我会选择 spinon 的答案(至少得到了我的投票),但它太短了,所以让我详细说明一下:

      将您的接口用于默认实现,然后使用依赖注入。大多数工具都允许您定义范围或一些标准来解决问题。

      我假设您在项目的早期就认识客户。因此,对于 ninject,您可能只想为每个客户端定义一个“模块”并将其加载到内核中,具体取决于客户端。

      所以我会创建一个“无自定义”模块,并为每个使用“Bind.To()”的特殊情况创建一个“ClientX”模块。 你最终得到了

      • 干净/默认的基本实现
      • 为新客户端更改一个地方(有一个新客户端?太好了。它可以使用默认值,或者只需要一个将接口映射到其他类的模块)

      其余代码不应该介意并通过注入获取依赖项(构造函数,属性,任何最容易使用的方法。构造函数可能是最好的方法)并且根本没有特殊处理。

      您甚至可以在 Ninject link text 中使用 conditional binding 来解决绑定问题,而无需使用不同的模块(尽管根据客户端的数量,这可能会变得混乱,最好分开)。

      【讨论】:

        【解决方案6】:

        我推荐访问者模式:

        http://en.m.wikipedia.org/wiki/Visitor_pattern

        以及中介者模式:

        http://en.m.wikipedia.org/wiki/Mediator_pattern

        原因是,根据您所说的,您可能会从类中的业务逻辑解耦或至少松散耦合中受益。

        【讨论】:

          【解决方案7】:

          正如@the_joric 所建议的那样,我将建议聚合而不是继承,但是您的描述听起来好像您的应用程序已经合理考虑了 - 没有很多小类等待从您的现有的类。假设是这种情况,对于任何给定的接口,如果您已经为新客户编写了一个完美的类并准备好了,我会说继续使用它。如果您出于某种原因担心这一点,请采用完美的类,使其抽象,并为您现有的客户和新客户创建空的子类 - 如果它不是非常合适,那么这就是我会的方式去吧。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-09-25
            • 1970-01-01
            • 2022-12-18
            • 2020-02-13
            • 1970-01-01
            • 2021-05-25
            • 2011-10-13
            • 1970-01-01
            相关资源
            最近更新 更多