Сравнение между делегированием в Java и Kotlin - PullRequest
0 голосов
/ 10 марта 2020

Я пытаюсь понять наследование и борьбу делегирования и как реализовать их обоих. У меня вопрос, почему я должен реализовать делегирование Kotlin путем с ключевым словом by? Я думаю, что это слишком много.

Позвольте привести пример. У нас есть приложение, с помощью которого мы можем заказать еду. Приложение называется DeliveryQueen.

Вот пример Java:

public interface FoodDeliverable {
    void deliverSomeFood();
}

public class PizzaCastle implements FoodDeliverable {
    @Override
    public void deliverSomeFood() {
        System.out.println("deliver the tastiest pizza in the world");
    }
}

public class BurgerCastle implements FoodDeliverable {
    @Override
    public void deliverSomeFood() {
        System.out.println("deliver the tastiest burger in the world");
    }
}

public class DeliveryQueen {
    private FoodDeliverable orderedFood;

    void chooseFood(FoodDeliverable food) {
        this.orderedFood = food;
    }

    void orderSomeFood() {
        this.orderedFood.deliverSomeFood();
    }
}

public class Main {

    public static void main(String[] args) {
        System.out.println("Hello and welcome ");

        DeliveryQueen dq = new DeliveryQueen();

        dq.chooseFood(new BurgerCastle());
        dq.orderSomeFood();
    }
}

Kotlin пример # 1 без явного использования ключевого слова:

interface FoodDeliverable {
    fun deliverSomeFood()
}

class PizzaCastle: FoodDeliverable {
    override fun deliverSomeFood() {
        print("deliver the tastiest pizza in the world")
    }
}

class BurgerCastle: FoodDeliverable {
    override fun deliverSomeFood() {
        print("deliver the tastiest burger in the world")
    }
}

class DeliveryQueen {
    private var orderedFood: FoodDeliverable = PizzaCastle()

    fun chooseFood(food: FoodDeliverable) {
        orderedFood = food
    }

    fun orderSomeFood() {
        orderedFood.deliverSomeFood()
    }
}

fun main() {
    println("Hello")

    val dq = DeliveryQueen()
    dq.chooseFood(BurgerCastle())
    dq.orderSomeFood()
}

Kotlin пример # 2 с явным использованием ключевого слова:

interface FoodDeliverable {
    fun deliverSomeFood()
}

class PizzaCastle: FoodDeliverable {
    override fun deliverSomeFood() {
        print("deliver the tastiest pizza in the world")
    }
}

class BurgerCastle: FoodDeliverable {
    override fun deliverSomeFood() {
        print("deliver the tastiest burger in the world")
    }
}

class DeliveryQueen(var orderedFood: FoodDeliverable): FoodDeliverable by orderedFood {
    //private var orderedFood: FoodDeliverable = PizzaCastle()

    fun chooseFood(food: FoodDeliverable) {
        orderedFood = food
    }

    fun orderSomeFood() {
        orderedFood.deliverSomeFood()
    }
}

fun main() {
    println("Hello")

    val dq = DeliveryQueen(PizzaCastle())
    dq.chooseFood(BurgerCastle())
    dq.orderSomeFood()
}

Я думаю, что оба моих kotlin примера работают нормально. Я не могу найти никакой разницы. Так зачем мне добавлять FoodDeliverable с заказанным продуктом, если у него нет преимуществ? Спасибо:)

Ответы [ 3 ]

1 голос
/ 10 марта 2020

В вашем примере делегирование используется не совсем так, как обычно, поэтому вы не видите преимущества.

DeliveryQueen перенаправляет вызов метода в экземпляр чего-либо, реализующего FoodDelivable, который является нормальным шаблоном; но ваш случай имеет два важных отличия:

  • он может изменить эту реализацию после создания, и
  • метод пересылки (orderSomeFood()) не тот, который он пересылает (deliverSomeFood()).

Если бы не это, вы могли бы реализовать этот класс просто:

class DeliveryQueen(orderedFood: FoodDeliverable): FoodDeliverable by orderedFood

… и без тела (кроме любых методов, которые вы необходимо добавить или переопределить).

(См. эти вопросы , почему Kotlin не поддерживает изменение делегата во время выполнения.)

Очевидно, что такой игрушечный пример не очень хорошо демонстрирует преимущества делегирования. Но представьте, если бы у FoodDeliverable было еще много методов; без поддержки делегирования Kotlin для DeliveryQueen потребуется множество шаблонов, таких как:

    fun deliverSomeFood() {
        orderedFood.deliverSomeFood()
    }

    fun payDriver(price: BigDecimal, tip: BigDecimal) {
        orderedFood.payDriver(price, tip)
    }

    fun leaveReview(comment: String, stars: NumberOfStars, promptness: EarlyOrLate, foodQuality: QualityMetric): Review {
        return orderedFood.leaveReview(comment, stars, promptness, foodQuality)
    }

    fun <T : ConnectionMethod> contactRestaurant(method: T): Connection<T> {
        return orderedFood.contactRestaurant(method)
    }

    // …and so on…

… все из которых необходимо поддерживать в актуальном состоянии.

(Количество методов может быть довольно большим. Например, я помню, что мне нужно было написать обертку в Java для java.sql.Connection, что более чем 50 методов ... Делегация сэкономила бы значительных шаблонных образцов! )

1 голос
/ 10 марта 2020

In Kotlin пример # 1 DeliveryQueen не является FoodDeliverable, потому что он не реализует интерфейс, поэтому это не правильная реализация композиции. Чтобы исправить Kotlin пример # 1 , вам нужно реализовать FoodDeliverable, который заставит вас переопределить его методы:

class DeliveryQueen: FoodDeliverable  {
    private var orderedFood: FoodDeliverable = PizzaCastle()

    override fun deliverSomeFood() {
        orderedFood.deliverSomeFood()
    }

    fun chooseFood(food: FoodDeliverable) {
        orderedFood = food
    }

    fun orderSomeFood() {
        orderedFood.deliverSomeFood()
    }

}

Посмотрите, как вам нужно реализовать deliverSomeFood() даже хотя все, что он делает, это передает вызов делегату orderedFood? здесь начинается делегирование kotlin с by, как в Kotlin пример # 2 , вы получаете реализацию по умолчанию, и у вас есть только override методы, где нужно добавить дополнительные логи c. Преимущество будет более очевидным, когда в вашем интерфейсе будет больше методов, в этом случае делегирование с помощью by поможет вам сэкономить много лишнего.

0 голосов
/ 10 марта 2020

Ах, хорошо. Я вижу свою проблему. Спасибо вам обоим:)

Итак, позвольте мне попытаться объяснить это. В Java вы должны явно вызывать интерфейсный метод. Таким образом, необходимо, чтобы DeliveryQueen вызывал метод DeliverySomeFood для FoodDeliverable.

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

Kotlin имеет ключевое слово by, что приводит к непосредственному вызову метода интерфейса без объезда?

Надеюсь, я правильно понимаю.

...