我经常遇到这样一种情况:我有一个抽象类型,需要根据它具体的具体实现进行不同的处理。
例如,abstract class Payment
可以被归类为class CreditCard
或class 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,只接受他们可以使用的子类。
有没有人有解决这个问题的简洁方法?
答案 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设置PaymentTaker
和Payment
之间的映射。
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%控制类的名称,这可能是个问题。