当唯一的差异是一个变量的类型时,重构重复的代码?

时间:2012-01-05 03:15:14

标签: c# api refactoring code-duplication

我必须能够连接两个不同版本的API(1.4和1.5),我们称之为Foo API。我连接到API并处理结果的代码基本上是重复的 - 唯一的区别是从两个API返回的数据类型。我怎样才能重构这个以消除重复?

在Foo14Connector.cs(我自己的类中调用1.4 API)

public class Foo14Connector
{
    public void GetAllCustomers() 
    {
        var _foo = new Foo14WebReference.FooService();
        Foo14WebReference.customerEntity[] customers = _foo.getCustomerList;
        foreach (Foo14WebReference.customerEntity customer in customers)
        {
            GetSingleCustomer(customer);
        }
    }
    public void GetSingleCustomer(Foo14WebReference.customerEntity customer)
    {
        var id = customer.foo_id;
        // etc 
    }
}

在几乎完全重复的类Foo15Connector.cs(我自己的类调用1.5 API)中

public class Foo15Connector
{
    public void GetAllCustomers() 
    {
        var _foo = new Foo15WebReference.FooService();
        Foo15WebReference.customerEntity[] customers = _foo.getCustomerList;
        foreach (Foo15WebReference.customerEntity customer in customers)
        {
            GetSingleCustomer(customer);
        }
    }
    public void GetSingleCustomer(Foo15WebReference.customerEntity customer)
    {
        var id = customer.foo_id;
        // etc 
    }
}

请注意,我必须有两个不同的连接器,因为API上的一个方法调用(数百个)在1.5中有一个新参数。

两个类Foo14WebReference.customerEntity和Foo15WebReference.customerEntity都具有相同的属性。

3 个答案:

答案 0 :(得分:5)

如果连接器位于不同的项目中,这很容易解决:

添加一个新的类文件,将其称为ConnectorCommon并复制所有公共代码,但删除了名称空间。将此类设为部分类,并将类(而不是文件)重命名为Connector。

您需要为每个项目添加一个链接。

接下来,从当前连接器类中删除所有代码,将类(不一定是文件)重命名为与partial类相同,并添加引用命名空间的using语句。

这应该得到你想要的东西。

所以,当你完成后,你将拥有:

文件ConnectorCommon:

public partial class Connector
{
    public void GetAllCustomers() 
    {
        var _foo = new FooService();
        customerEntity[] customers = _foo.getCustomerList;
        foreach (customerEntity customer in customers)
        {
            GetSingleCustomer(customer);
        }
    }
    public void GetSingleCustomer(customerEntity customer)
    {
        var id = customer.foo_id;
        // etc 
    }
}

文件Magento15Connector

using Foo15WebReference;

partial class Connector
{
}

文件Magento14Connector

using Foo14WebReference;

partial class Connector
{
}

<强>更新

这个过程起初可能有点令人困惑。

为了澄清,您将在两个项目之间的共同文件中共享源代码

实际的类是每个项目中具有命名空间的特定类。您可以使用partial关键字将公共文件与每个项目中的实际项目文件(即Magneto14)组合在一起,以便在编译时在该项目中创建完整的类。

最棘手的部分是将公共文件添加到两个项目中。

要执行此操作,请在第二个项目中选择Add Existing Item...菜单,导航到公共文件,然后单击Add按钮旁边的右箭头。

从下拉菜单中选择Add as link。这会将引用添加到第二个项目的文件中。源代码将包含在两个项目中,对公共文件的任何更改都将在两个项目中自动提供。

更新2

我有时会忘记VB如何轻松地完成这样的任务,因为这是我普通的编程环境。

为了在C#中完成这项工作,还有一个必须采用的技巧:Conditional compilation symbols。它使公共代码的开始比我想要的更冗长,但它仍然确保你可以使用一组公共代码。

要使用此技巧,请为每个项目添加条件编译符号(确保为All Configurations设置)。例如,在Magento14项目中,添加Ver14并在Magento15项目中添加Ver15

然后在公共文件中,使用类似于以下内容的结构替换命名空间:

#if Ver14
using Magneto14;
namespace Magento14Project

#elif Ver15
using Magneto15;
namespace Magento15Project

#endif

这将确保根据编译公共代码的项目包含正确的命名空间和用法。

请注意,所有常见using语句都应保留在公共文件中(即足以使其编译)。

答案 1 :(得分:1)

如果FooConnectors没有密封并且您可以控制创建新实例,那么您可以同时派生自己的连接器并实现接口。在c#中,您可以通过简单地从基类继承它们来实现成员!

public IFooConnector {
    void GetAllCustomers();
}

public MyFoo14Connector : Foo14Connector, IFooConnector
{
    // No need to put any code in here!
}

然后

IFooConnector connector = new MyFoo14Connector();
connector.GetAllCustomers();

答案 2 :(得分:0)

您应该引入两个实现共有的接口。如果项目使用相同的语言编写并且位于不同的项目中,则可以引入两个项目都引用的公共项目。然后,您将转向仅依赖于您的接口,这应该允许您使用控制反转(谷歌,依赖注入或服务定位器或工厂模式)在幕后交换不同的实现。

您的困难可能是:

1)实现中的公共静态方法无法通过接口静态公开 2)在一个实现类中可能有代码,即Foo14Connector或Foo15Connector,它们没有意义放入通用接口