C#重构:除了使用不同的接口外,你将如何重构2个相同的方法

时间:2010-08-05 15:50:38

标签: c# refactoring

必须拥有2个不同的接口。

你会如何重构这个?

我应该重构这段代码吗?

private void CreateInstanceForProviderA()
{
    a = FactorySingleton.Instance.CreateInstanceA("A");

    if (a == null)
    {
        ShowProviderNotInstanciatedMessage();
        return;
    }

    a.Owner = Handle.ToInt32();
    lbl_Text.Text = a.Version();
}

private void CreateInstanceForProviderB()
{
    b = FactorySingleton.Instance.CreateInstanceB("B");

    if (b == null)
    {
        ShowProviderNotInstanciatedMessage();
        return;
    }

    b.Owner = Handle.ToInt32();
    lbl_Text.Text = b.Version();
}

如果有一个共同的界面,我可以写:

private void CreateInstanceForProvider(string provider)
{

    p = FactorySingleton.Instance.CreateInstanceB(provider);
    // p is shared over the whole class
    if (p == null)
    {
        ShowProviderNotInstanciatedMessage();
        return;
    }

    var tmpProvider = p as ICommonProvider;

    tmpProvider .Owner = Handle.ToInt32();
    lbl_Text.Text = tmpProvider .Version();
}

11 个答案:

答案 0 :(得分:5)

嗯,首先要做的是对FactorySingleton的作者大吼大叫修复该死的代码,以便ClassA和ClassB为其公共字段提供一个通用接口。

与此同时,你几乎坚持使用反射,这很难看,而且不值得这么做。

答案 1 :(得分:5)

您使用的是哪个版本的C#?

在C#4(Visual Studio 2010)中,新的dynamic关键字可以帮助共享代码。如果这是一个性能关键的代码部分,我不会使用,但如果这只是运行了几次,那么继续。

答案 2 :(得分:3)

InstanceA和InstanceB应该实现一个通用接口。

public interface IA : ICommon {...}
public interface IB : ICommon {...}

public interface ICommon
{
    int Owner {get;}
    string Version();
}

这样,你仍然有两个不同的接口,但这些接口的常见方面的定义方式是你可以用它们做同样的事情。

答案 3 :(得分:1)

也许有第三种私有方法,并用对第三种方法的调用替换你的代码,如下所示:

private void CreateInstanceForProviderA() {     return DoSomething(); }

private void CreateInstanceForProviderB() {     return DoSomething(); }

答案 4 :(得分:1)

public interface ICommon
{
    int Owner { get; }
    string Version();
}

public interface IA : ICommon
public interface IB : ICommon

private void CreateInstanceForProvider(ICommon c) 
{ 
    if (c == null) 
    { 
        ShowProviderNotInstanciatedMessage(); 
        return; 
    } 

    c.Owner = Handle.ToInt32(); 
    lbl_Text.Text = c.Version(); 
} 

答案 5 :(得分:1)

打击类型系统总是很痛苦。不使用dynamic,这是我的尝试。

鉴于您为ab提供了这两个不同的界面:

interface IA {
  int Owner { set; }
  string Version();
}
interface IB {
  int Owner { set; }
  string Version();
}

您可以创建这样的包装器类型:

class WrapperAB : IA, IB {
  IA a; IB b;
  public WrapperAB(object o) {
    if (o is IA) a = (IA)o;
    else if (o is IB) b = (IB)o;
    else throw new Exception();
  }
  public int Owner {
    set { 
      if (a != null) a.Owner = value; 
      else b.Owner = value; 
    }
  }
  public string Version() {
    if (a != null) return a.Version();
    else return b.Version();
  }
}

将您的方法更改为:

private void CreateInstanceForProviderA() {
  CreateInstanceForProvider<IA>("A", FactorySingleton.Instance.CreateInstanceA, out a);
}

private void CreateInstanceForProviderB() {
  CreateInstanceForProvider<IB>("B", FactorySingleton.Instance.CreateInstanceB, out b);
}

private void CreateInstanceForProvider<TI>(string name, Func<string, TI> factory, out TI instance) {
  instance = factory(name);

  if (instance == null) {
    ShowProviderNotInstanciatedMessage();
    return;
  }

  var w = new WrapperAB(instance);
  w.Owner = Handle.ToInt32();
  lbl_Text.Text = w.Version();
}

答案 6 :(得分:0)

我会保持原样,不需要重构...... YET!
如果/当ProviderC出现时,那么我会重构。 8)

答案 7 :(得分:0)

这完全取决于那些方法调用(.CreateInstanceA.CreateInstanceB)。

如果他们以相同的方式做同样的事情,唯一的区别是字符串参数,那么是:将方法重构为CreateInstanceForProvider(string providerCode)并让用户/调用代码传入适当的参数。

如果他们做了稍微不同的事情,那么你可能仍然能够重构,而且它变得更加令人头疼。此时,您必须确定增加的抽象(以及添加新的Providers的额外简易性)是否值得重构并且必须重新运行(并且可能重写)任何必要的测试。

答案 8 :(得分:0)

编辑:这没有用,因为a和b是不同的类型(不确定是在我给出答案之前或之后...)

我假设a和b是字段,而不是属性。

抽象地说,将通用功能放在两个原始方法调用的单个方法中:

private void CreateInstanceForProviderA()
{
    a = CreateInstanceForProvider("A");
}

private void CreateInstanceForProviderB()
{
    b = CreateInstanceForProvider("B");
}

private FactorySingleton CreateInstanceForProvider(string which) 
{
    var instance = FactorySingleton.Instance.CreateInstanceB(which);

    if (instance == null)
    {
        ShowProviderNotInstanciatedMessage();
        return;
    }

    instance.Owner = Handle.ToInt32();
    lbl_Text.Text = instance.Version(); 

    return instance;
}

答案 9 :(得分:0)

我会传入一个枚举来指定要调用的创建实例代码。之后,创建一个包装类,其中包含使用反射获取/设置的方法,如其他答案中所建议的那样使用反射。不确定它是否值得,因为您的代码可能比以前更难阅读。

    public enum Provider
    {
        A,
        B
    }

    private void CreateInstanceForProvider(Provider provider)
    {
        ProviderWrapper provider = null;

        switch (provider)
        {
            case Provider.A:
                provider = new ProviderWrapper(FactorySingleton.Instance.CreateInstanceA("A"));
                break;
            case Provider.B:
                provider = new ProviderWrapper(FactorySingleton.Instance.CreateInstanceB("B"));
                break;
        }

        if (provider == null)
        {
            ShowProviderNotInstanciatedMessage();
            return;
        }

        provider.SetOwner(Handle.ToInt32());
        lbl_Text.Text = provider.GetVersion();
    }

    public class ProviderWrapper
    {
        private readonly object _original;

        public ProviderWrapper(object original)
        {
            _original = original;
        }

        public void SetOwner(int value)
        {
            _original.GetType().GetProperty("Owner").SetValue(_original, value, null);
        }

        public string GetVersion()
        {
            return (String)_original.GetType().GetProperty("Owner").GetValue(_original, null);
        }
    }

答案 10 :(得分:0)

删除重复的代码。在这种情况下,您应该能够删除执行空检查的中间消息块以及失败的实例化消息。

private void CreateInstanceForProviderA()
{
    a = FactorySingleton.Instance.CreateInstanceA("A");

    if (IsNullObject(a))
    {
       return;
    }

    a.Owner = Handle.ToInt32();
    lbl_Text.Text = a.Version();
}

private void CreateInstanceForProviderB()
{
    b = FactorySingleton.Instance.CreateInstanceB("B");

    if (IsNullObject(b))
    {
       return;
    }

    b.Owner = Handle.ToInt32();
    lbl_Text.Text = b.Version();
}

private bool IsNullObject(object obj)
{
    if (obj == null)
    {
        ShowProviderNotInstanciatedMessage();
        return true;
    }
    return false;
}

如果您确实找到了在这些提供商上提供通用接口或共享虚拟方法的方法,我们可以更积极地进行重构。