Как сделать так, чтобы один класс мог быть создан только с объектом другого без раскрытия внутренних элементов? - PullRequest
1 голос
/ 07 марта 2019

Допустим, у нас есть два класса, Foo и Bar.Bar должен быть конструктивно только из Foo объекта.

Вот мои текущие попытки с той причиной, по которой я не считаю их правильными решениями:


Попытка 1:

class Foo {
    var data: Int = 0 // meh: This implementation detail should not be exposed to the public.
    fun add(x: Int) {
        data += x
    }
}

class Bar(foo: Foo) {
    private var data: Int = 2 * foo.data
    fun show() = println(data + 10)
}

fun main() {
    val myFoo = Foo()
    myFoo.add(3)
    myFoo.add(4)
    val myBar = Bar(myFoo)
    myBar.show()
}

Проблема: Foo::data не должна быть видна снаружи.


Попытка 2:

class Foo {
    private var data: Int = 0
    fun add(x: Int) {
        data += x
    }

    fun makeBar() = Bar(2 * data)
}

class Bar(private val data: Int) { // meh: Bar should only be instantiated from a Foo.
    fun show() = println(data + 10)
}

fun main() {
    val myFoo = Foo()
    myFoo.add(3)
    myFoo.add(4)
    val myBar = myFoo.makeBar()
    myBar.show()
}

Проблема: каждый можетсоздайте Bar сейчас, даже не имея Foo под рукой.


Исходя из C ++, моей первой идеей для решения этой проблемы было бы сделать конструктор Bar закрытым, но пустьтолько Foo получить к нему доступ, объявив его классом friend.Возможно ли что-то подобное в Котлине, или есть другое (лучшее) решение?

1 Ответ

4 голосов
/ 07 марта 2019

Читая ваш сценарий использования, вы, вероятно, ищете внутренний класс тогда.Вот решение, полученное из вашей первой попытки с использованием внутреннего класса Bar:

class Foo {
    private var data = 0 // private as you do not want to make it publicly available
    fun add(x: Int) {
        data += x
    }

    inner class Bar {
        private var data = 2 * this@Foo.data // access the outer foo directly... no need to hold it in the constructor
        fun show() = println(data + 10)
    }
}    

fun main() {
    val myFoo = Foo()
    myFoo.add(3)
    myFoo.add(4)
    // val myBar = Foo.Bar() // this does not compile... you need a foo to build a bar...
    val myBar = myFoo.Bar() // this works...
    myBar.show()
    // myBar.data // this wouldn't compile as it's not accessible
}

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

class Foo {
    private var data = 0
    fun add(x: Int) {
        data += x
    }
    fun Bar.getFooData() = data
}

class Bar(foo: Foo) {
    private var data = 2 * with(foo) { getFooData() }
    fun show() = println(data + 10)
}

Таким образом, вы можете получить доступ к данным Foo, только если у вас есть оба, a Bar и a Foo доступно, последний как приемник.Это, однако, также означает, что вы можете получить доступ к Foo.data извне, если вам доступны оба объекта, например, следующее будет также в main:

with (myFoo) {
  myBar.getFooData()
}

То есть: data недоступенснаружи (игнорируя размышления) вложенный или внутренний класс - ваш друг.Сложнее получить к нему доступ (или: заставить некоторые условия быть верными перед его использованием), подход с использованием функции расширения с соответствующим получателем может быть вашим другом.

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