在给定其泛型类型参数的类型的情况下动态选择组件的类型

时间:2010-08-06 13:23:14

标签: c# design-patterns generics

我经常遇到这样一种情况:我有一个抽象类型,需要根据它具体的具体实现进行不同的处理。

例如,abstract class Payment可以被归类为class CreditCardclass StoredCredit。要实际处理付款,我们希望使用

的实现
interface IPaymentTaker {
  PaymentProcessingResult Process(Payment payment); }

即。任

class CreditCardPaymentTaker : IPaymentTaker { ... }

class StoredCreditPaymentTaker : IPaymentTaker { ... }

过去我已将IDictionary注入父组件,然后完成

_paymentTakers[payment.GetType()].Process(payment);

这样做的缺点是IPaymentTaker实现的类型不够强,所以Process方法的第一位必须是:

Process(Payment payment)
{
  var creditCardPayment = payment as CreditCardPayment;
  if (creditCardPayment == null)
    throw new Exception("Payment must be of type CreditCard");
}

我确信我要尝试实施的模式必须有一个名称,但我不知道它是什么!

理想情况下我会

(a)能够仅根据付款类型实例化PaymentProcessor,而无需创建字典;

(b)能够拥有强类型的PaymentProcessors,只接受他们可以使用的子类。

有没有人有解决这个问题的简洁方法?

4 个答案:

答案 0 :(得分:2)

interface IPayment
{
 IPaymentTaker Taker {get;}
}

class CreditCardPayment : IPayment
{
  IPaymentTaker Taker{ get {return new CreditCardPaymentTaker();}}
}

payment.Taker.Process(payment); 

答案 1 :(得分:2)

您可以使用visitor

解决此问题
interface IPaymentVisitor {
  void Visit(CreditCard payment);
  void Visit(StoredCredit payment);
}

abstract class Payment {
  public abstract void Accept(IPaymentVisitor visitor);
}
class CreditCard : Payment {
  public override void Accept(IPaymentVisitor visitor) {
    visitor.Visit(this);
  }
}
class StoredCredit : Payment {
  public override void Accept(IPaymentVisitor visitor) {
    visitor.Visit(this);
  }
}

class PaymentTaker : IPaymentVisitor, IPaymentTaker {
  public void Visit(CreditCard payment) {
    // ... 
  }

  public void Visit(StoredCredit payment) {
    // ... 
  }

  public PaymentProcessingResult Process(Payment payment) {
    payment.Accept(this);
    // ...
  }
}

如果您仍希望将不同的付款方分开,或者您的层次结构抖动,则可以使用acyclic visitor (pdf)

interface IPaymentVisitor {
}

interface IPaymentVisitor<TPayment> : IPaymentVisitor where TPayment : Payment {
  void Visit(TPayment payment);
}

abstract class Payment {
  public abstract void Accept(IPaymentVisitor visitor);
}
class CreditCard : Payment {
  public override void Accept(IPaymentVisitor visitor) {
    if (visitor is IPaymentVisitor<CreditCard>) {
      ((IPaymentVisitor<CreditCard>)visitor).Visit(this);
    }
  }
}
class StoredCredit : Payment {
  public override void Accept(IPaymentVisitor visitor) {
    if (visitor is IPaymentVisitor<StoredCredit>) {
      ((IPaymentVisitor<StoredCredit>)visitor).Visit(this);
    }
  }
}

class CreditCardPaymentTaker : IPaymentVisitor<CreditCard>, IPaymentTaker {
  public void Visit(CreditCard payment) {
    // ...
  }
  public PaymentProcessingResult Process(Payment payment) {
    payment.Accept(this);
    // ...
  }
}
class StoredCreditPaymentTaker : IPaymentVisitor<StoredCredit>, IPaymentTaker {
  public void Visit(StoredCredit payment) {
    // ...
  }
  public PaymentProcessingResult Process(Payment payment) {
    payment.Accept(this);
    // ...
  }
}

答案 2 :(得分:1)

尽管詹姆斯的方法很理想,但使用IoC容器可能很困难。这是我基于反射或动态的方法。执行以下操作将允许您仍然使用IoC设置PaymentTakerPayment之间的映射。

public class Payment
{

}

public class CreditCardPayment : Payment
{

}

public class StoreCreditPayment : Payment
{

}

public interface IPaymentTaker
{

}

public interface IPaymentTaker<T> : IPaymentTaker
{
    void Process(T payment);
}

public static class PaymentTaker
{
    public static void Process(Payment payment)
    {
        var paymentType = payment.GetType();

        // You would have these already setup and loaded via your IOC container...
        var paymentTakers = new Dictionary<Type, IPaymentTaker>();
        paymentTakers.Add(typeof(CreditCardPayment), new CreditCardPaymentTaker());
        paymentTakers.Add(typeof(StoreCreditPayment), new StoreCreditPaymentTaker());

        // Get the payment taker for the specific payment type.
        var paymentTaker = paymentTakers[paymentType];

        // Execute the 'Process' method.
        paymentTaker.GetType().GetMethod("Process").Invoke(paymentTaker, new object[]{ payment });
        // If .NET 4.0 - dynamics can be used.
        // dynamic paymentTaker = paymentTakers[paymentType];
        // paymentTaker.Process((dynamic)payment);

    }
}

public class CreditCardPaymentTaker : IPaymentTaker<CreditCardPayment>
{
    public void Process(CreditCardPayment payment)
    {
        Console.WriteLine("Process Credit Card Payment...");
    }
}

public class StoreCreditPaymentTaker : IPaymentTaker<StoreCreditPayment>
{
    public void Process(StoreCreditPayment payment)
    {
        Console.WriteLine("Process Credit Card Payment...");
    }
}

然后你可以像这样使用它:

var cc = new CreditCardPayment();
PaymentTaker.Process(cc);

答案 3 :(得分:0)

如果您可以确保Payment和PaymentTaker匹配的名称,您可以使用以下内容:

Process(Payment payment)
{
    String typeName = "YourPathToPaymentTakers." + payment.GetType().Name + "Taker";
    Type type = typeof(IPaymentTaker).Assembly.GetType(typeName);                       
    IPaymentTaker taker = (IPaymentTaker)Activator.CreateInstance(type);;
}

我过去曾使用过这种方法,但是如果你没有100%控制类的名称,这可能是个问题。