Когда кастинг / экземпляр хорошего использования? - PullRequest
0 голосов
/ 07 февраля 2019

Каждый раз, когда я гугляю instanceof и читаю, я всегда вижу ответы, в которых говорится, что нужно избегать этого и использовать шаблон X.

У меня есть пример, в котором я не вижу ни одного шаблона, который, как мне кажется, мог бы использовать.

У нас есть 2 класса: Order и Payment (CashPayment и CardPayment).

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 и кастинга?Если да, то как мы можем достичь этого "OO-way"?Если нет, я предполагаю, что это один из действительных случаев?

IMO, мы можем избежать instanceof / casting и одобрить использование переопределенных методов, однако, если вы хотите знать о конкретном объекте, этого нельзя избежать.

Редактировать:

Я пытаюсь написать свои доменные модели, что означает, что он не зависит от инфраструктуры и конкретных приложений.

Представьте, что нам нужноДля сохранения Заказа через OrderRepository и Платеж есть свои таблицы.Разве это не было бы ужасно, если бы это было похоже на

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 голосов
/ 07 февраля 2019

Вы могли бы пойти с композицией над наследованием .

Возможно, что-то вроде:

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) { }.

0 голосов
/ 08 февраля 2019

Если вы абсолютно хотите отделить операцию от самого объекта (например, чтобы сохранить разделение интересов), но операция тесно связана с деталями подкласса, тогда у вас есть только два варианта.

Вам либо нужно переосмыслить модель и найти однородную абстракцию, которая может быть любым подходом, позволяющим обрабатывать различные типы одинаково.

например,

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

или вам нужно выполнить какое-либо сопоставление типов, используете ли вы шаблон посетителя, сопоставление с шаблоном, instanceof и т. Д.

, например, с шаблоном посетителя

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);
0 голосов
/ 07 февраля 2019

Очевидное объектно-ориентированное решение состоит в том, чтобы добавить display() метод к Payment.

. В общем случае экземпляр / casting не одобряется, поскольку он обычно указывает на неоптимальный дизайн.Единственный раз, когда это разрешено, это когда система типов не достаточно сильна, чтобы что-то выразить.Я сталкивался с несколькими ситуациями в Java, где нет лучшего решения (в основном потому, что в Java нет коллекций только для чтения, поэтому универсальный параметр является инвариантным), ни в Scala, ни в Haskell пока нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...