Как реализовать шаблон Null Object в классе данных в Kotlin?

У меня есть класс данных Kotlin:

data class PaymentAccount(
    val accountId: Int,
    val accountNumber: String,
    val title: String
)

Вот что я бы сделал в Java:

Создайте абстрактный класс:

public abstract class PaymentAccount {

    protected int accountId;
    protected String accountNumber;
    protected String title;

    public PaymentAccount(int accountId,
                          String accountNumber,
                          String title) {
        this.accountId = accountId;
        this.accountNumber = accountNumber;
        this.title = title;
    }
}

Создать нулевой объект и расширить абстрактный класс:

public class NullPaymentAccount extends PaymentAccount {

    public NullPaymentAccount() {
        super(-1,
                "Invalid account number",
                "Invalid title");
    }
}

Создайте реальный объект и расширьте абстрактный класс:

public class RealPaymentAccount extends PaymentAccount {

    public RealPaymentAccount(int accountId,
                              String accountNumber,
                              String title) {
        super(accountId,
                accountNumber,
                title);
    }
}

Как правильно реализовать шаблон Null Object в Kotlin? Есть ли более чем один способ? Если да, то какой самый краткий и элегантный способ?

4 ответа

Решение

В Kotlin вы можете сделать то же самое, просто с меньшим количеством строк кода:

interface Account {
    val accountId: Int
    val accountNumber: String
    val title: String
}

object EmptyAccount : Account {
        override val accountId: Int = 1
        override val accountNumber: String = ""
        override val title: String = ""
}

data class PaymentAccount(
        override val accountId: Int,
        override val accountNumber: String,
        override val title: String): Account

Обратите внимание, что мы также делаем EmptyAccount синглетон для эффективности.

Несмотря на то, что решение, которое вам было дано, уже подойдет, вам может не понадобиться явный класс для представления нулевого состояния, если ваши значения по умолчанию - не все или ничего. Я бы сказал, что предоставление значений по умолчанию для всех ваших полей - это шаблон нулевого объекта.

Например, вы можете написать свой класс следующим образом:

data class PaymentAccount(
    val accountId: Int = -1,
    val accountNumber: String = "Invalid Account Number",
    val title: String = "Invalid Title"
)

И когда вы их строите:

PaymentAccount(1, "Acct2", "Checking") // Actual data
PaymentAccount() // Default values, therefore a "null" object.

Единственная реальная проблема - когда вы указываете только некоторые значения, но это может быть хорошо / желательно:

PaymentAccount(1) // accountNumber and title have defaults

Лучшее использованиеsealed interfaceилиsealed class. Например:

      sealed interface Account {

        val accountId: Int
        val accountNumber: String
        val title: String

        object Empty : Account {
            override val accountId: Int = 1
            override val accountNumber: String = ""
            override val title: String = ""
        }

        data class Payment(
            override val accountId: Int,
            override val accountNumber: String,
            override val title: String
        ) : Account
    }

Использование:

          val account = findAccountById(id = 1)

    when (account) {
        Account.Empty -> TODO()
        is Account.Payment -> TODO()
    }
        

Где:

       val list = listOf(
        Account.Payment(
            accountId = 1,
            accountNumber = "1",
            title = "Bob"
        )
    )

 private fun findAccountById(id: Int): Account {
        return list.firstOrNull { it.accountId == id }?:Account.Empty
    }

Другим способом может быть определение вторичного конструктора для этого:

      data class PaymentAccount(
    val accountId: Int,
    val accountNumber: String,
    val title: String
){
  constructor() : this(-1,
                  "Invalid account number",
                  "Invalid title")
}
Другие вопросы по тегам