我该如何重构这个?

时间:2010-06-29 20:47:50

标签: c# .net oop refactoring interface

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

随着最新客户的加入,我注意到会有很多来自其他客户的代码重复,但另一位客户与其他客户无关。

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

我的问题是我如何重构这个仍然保持代码清洁?如果我是这个代码库的新手,我希望每个客户使用这些类的默认或他们自己的实现......但这是很多重复。

7 个答案:

答案 0 :(得分:5)

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

你的问题实在太模糊了,无法全面回答,但这里是你如何利用继承。

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

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

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


我的主要观点是OOP在基础/抽象/具体类中存在多少或多少功能。没有银弹答案,有时你的基础类将是骷髅,有时他们会完全充实;这一切都取决于手头的具体问题。

答案 1 :(得分:2)

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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

STW建议我应该对“战略”的含义做一些澄清,以及它与正常继承的区别。我认为继承是你非常熟悉的东西 - 基类中的某些东西(通常是一个方法 - abstractvirtual)被派生类中的替代实现替换。

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

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

答案 4 :(得分:1)

我推荐访客模式:

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

以及调解员模式:

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

原因在于,根据您所说的内容,您可能会从类中解除业务逻辑,或者至少更松散耦合,从而获益。

答案 5 :(得分:0)

我会选择spinon的答案(至少得到我的投票),但是要做空,所以让我详细说明:

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

我假设您在程序的某个早期阶段确实了解客户端。因此,对于ninject,您可能只想为每个客户端定义一个“模块”并将其加载到内核中,具体取决于客户端。

所以我创建了一个“no customization”模块,并为每个使用'Bound.To()`的特殊情况创建一个“ClientX”模块。 你最终得到了

  • 一个干净/默认的基础实现
  • 新客户端的单个地点更改(获得一个新客户端?很棒。无论是使用默认设置还是只需要一个将接口映射到其他类的模块)

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

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

答案 6 :(得分:0)

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