重构切换类型代码气味添加方法时键入似乎不合适

时间:2012-01-14 22:42:49

标签: oop polymorphism refactoring

假设我有以下方法,给定PaymentType,向需要撤回付款的每个工厂发送适当的付款请求:

public void SendRequestToPaymentFacility(PaymentType payment) {

    if(payment is CreditCard) {
      SendRequestToCreditCardProcessingCenter();
    } else if(payment is BankAccount) {
      SendRequestToBank();
    } else if(payment is PawnTicket) {
      SendRequestToPawnShop();
    }

}

显然这是代码嗅觉,但在寻找适当的重构时,我所见过的唯一例子涉及在条件中执行的代码显然是类本身的责任的情况,例如:给出标准示例:

public double GetArea(Shape shape) {
  if(shape is Circle) {
    Circle circle = shape As Circle;

    return circle.PI * (circle.radius * circle.radius);
  } else if(shape is Square) {
    Square square = shape as Square;    

    return square.length * square.width;
  }
}
对于每个Shape子类,

GetArea()似乎是一个非常合理的责任,当然可以很好地重构:

public class Shape
{
  /* ... */
  public abstract double GetArea();
}

public class Circle
{

  public override double GetArea()
  {
    return PI * (radius * radius);
  }

}

但是, SendRequestToPaymentFacility()似乎不适合PaymentType拥有。(并且似乎违反了单一责任原则)。但我需要根据PaymentFacility的类型向适当的PaymentType发送请求 - 这样做的最佳方式是什么?

3 个答案:

答案 0 :(得分:1)

您可以考虑在CandyBar类中添加属性或方法,以指示CandyBar是否包含坚果。现在,您的GetProcessingPlant()方法无需了解不同类型的CandyBars。

public ProcessingPlant GetProcessingPlant(CandyBar candyBar) {

    if(candyBar.ContainsNuts) {
        return new NutProcessingPlant();
    } else {
        return new RegularProcessingPlant();
    }

}

答案 1 :(得分:0)

一种选择是将IPaymentFacility接口参数添加到各个PaymentType后代的构造函数中。基本PaymentType可以具有抽象的PaymentFacility属性;基类型上的SendRequestToPaymentFacility将委托:

public abstract class PaymentType
{
    protected abstract IPaymentFacility PaymentFacility { get; }

    public void SendRequestToPaymentFacility()
    {
        PaymentFacility.Process(this);
    }
}

public interface IPaymentFacility
{
    void Process(PaymentType paymentType);
}

public class BankAccount : PaymentType
{
    public BankAccount(IPaymentFacility paymentFacility)
    {
        _paymentFacility = paymentFacility;
    }

    protected override IPaymentFacility PaymentFacility
    {
        get { return _paymentFacility; }
    }

    private readonly IPaymentFacility _paymentFacility;
}

您可以使用DI / IoC容器库,而不是手动连接依赖注入。配置它以便BankAccount获得银行等

缺点是支付工具只能访问基类PaymentType的公共(或可能是内部)成员。

修改

您实际上可以使用泛型来获得后代类成员。要么使SendRequestToPaymentFacility抽象(摆脱抽象属性),要么得到幻想:

public abstract class PaymentType<TPaymentType>
    where TPaymentType : PaymentType<TPaymentType>
{
    protected abstract IPaymentFacility<TPaymentType> PaymentFacility { get; }

    public void SendRequestToPaymentFacility()
    {
        PaymentFacility.Process((TPaymentType) this);
    }
}

public class BankAccount : PaymentType<BankAccount>
{
    public BankAccount(IPaymentFacility<BankAccount> paymentFacility)
    {
        _paymentFacility = paymentFacility;
    }

    protected override IPaymentFacility<BankAccount> PaymentFacility
    {
        get { return _paymentFacility; }
    }

    private readonly IPaymentFacility<BankAccount> _paymentFacility;
}

public interface IPaymentFacility<TPaymentType>
    where TPaymentType : PaymentType<TPaymentType>
{
    void Process(TPaymentType paymentType);
}

public class Bank : IPaymentFacility<BankAccount>
{
    public void Process(BankAccount paymentType)
    {
    }
}

这里的缺点是将银行与BankAccount类联系起来。

另外,Eric Lippert discourages this,他提出了一些很好的观点。

答案 2 :(得分:0)

您可以采取的一种方法是使用命令模式。在这种情况下,您将创建并排队相应的命令(例如信用卡,银行帐户,典当票)而不是调用特定方法。然后,您可以为每个将执行相应操作的命令配备单独的命令处理器。

如果您不想要条件复杂性,您可以提出一种包含支付类型作为属性的命令,然后命令处理器可以负责确定如何处理该请求(使用适当的支付处理器。)

其中任何一项都可以帮助您的班级通过移出付款处理的详细信息来关注Single Responsibility Principle