什么时候可以很好地使用铸造/实例?

时间:2019-02-07 03:21:08

标签: oop polymorphism domain-driven-design

每次使用google instanceof和cast进行搜索时,我总是会看到回答,避免使用X模式。

我有一个示例,其中看不到我认为可以使用的任何模式。

我们有2类:订单和付款(现金付款和卡付款)。

CashPayment具有1个名为amount的属性和一个已实现的方法pay

CardPayment具有1个名为cardNumber的属性和一个已实现的pay,该属性调用了第三方API。

现在说您想撰写有关订单的视图,有人会如何避免在此处使用instanceof或强制转换来显示付款明细?

使用instanceof可以做到:

order = new Order(...);
order.checkout(aPayment);

Payment Details (Cash):
Type: (instanceof CashPayment ? "Cash") or order.payment().type();
Amount: ((CashPayment) order.payment()).amount();

Payment Details (Card):
Type: (instanceof CardPayment ? "Card") or order.payment().type();
Card Number: ((CardPayment) order.payment()).cardNumber();

问题:我们真的可以避免执行instanceof和cast吗?如果是的话,我们如何实现这一“ OO路”?如果没有,我认为这是有效的情况之一?

IMO,我们可以避免instanceof / casting并赞成使用重写的方法,但是,如果您想知道一个具体的对象,那是不可避免的。

编辑:

我正在尝试编写域模型,这意味着它与基础架构和特定于应用程序的内容无关。

想象一下,我们需要通过OrderRepository保存Order,并且Payment具有自己的表。像这样不是很丑吗?

class OrderRepository {
  public function save(Order order) {
    // Insert into order query here...
    // Insert into orderItems query here...
    // Insert payment into its table

    queryBuilder
      .table(order.payment().tableName())
      .insert([
        order.payment().columnName() => order.payment().value()
      ]);
  }
}

3 个答案:

答案 0 :(得分:0)

显而易见的面向对象的解决方案是在display()上添加Payment方法。

通常来说,/ casting的实例是令人头疼的,因为它通常表示次优的设计。唯一允许的时间是类型系统的功能不足以表达某些内容。我在Java中遇到了几种情况,没有更好的解决方案(主要是因为Java中没有只读集合,因此泛型参数是不变的),而Scala或Haskell中都没有。

答案 1 :(得分:0)

您可以通过继承使用组成

也许是这样的:

public class Payment
{
    private CardPaymentDetail _cardPaymentDetail;

    public PaymentType Type { get; private set; }
    public decimal Amount { get; }

    private Payment(decimal amount)
    {
        // > 0 guard

        Amount = amount;
    }

    private Payment(decimal amount, CardPaymentDetail cardPayment)
        : this(amout)
    {
        // null guard

        CardPayment = cardPayment;
    }

    public CardPaymentDetail CardPayment
    {
        get 
        {
            if (Type != PaymentType.Card)
            {
                throw new InvalidOperationException("This is not a card payment.");
            }

            return _cardPaymentDetail;
        }
    }
}

恕我直言,持久性也可能更容易。同样,我还使用了等同于Unknown的付款方式作为默认付款方式,然后使用一种方法来指定付款方式:AsCard(CardPaymentDetail cardPayment) { }

答案 2 :(得分:0)

如果您绝对希望将操作与对象本身分开(例如,保持关注点分离),但是操作与子类详细信息紧密耦合,那么您只有两种选择。

您要么需要重新考虑模型并找到同构的抽象,这可以是允许您以相同方式处理各种类型的任何方法。

例如

Payment Details:
Type: {{payment.type}}
{{for attr in payment.attributes}}
    {{attr.name}}: {{attr.value}}
{{/}}

或者您需要执行某种类型匹配,无论您使用的是访客模式,模式匹配,instanceof等。

例如Visitor Pattern

interface IPaymentVisitor {
    public void visit(CashPayment payment);
    public void visit(CardPayment payment);
}

class PaymentRenderer implements IPaymentVisitor ...

class CashPayment extends Payment {
   ...
   public void visit(IPaymentVisitor visitor) {
       visitor.visit(this);
   }
}

var renderer = new PaymentRenderer(outputStream);
payment.accept(renderer);