From dc2859660d7ff3dce9e875bffa763f521d4df973 Mon Sep 17 00:00:00 2001 From: Marcin Moskala Date: Tue, 27 Mar 2018 00:07:20 +0200 Subject: [PATCH 1/3] Propose default values for properties in interfaces --- ...ult-values-for-properties-in-interfaces.md | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 proposals/default-values-for-properties-in-interfaces.md diff --git a/proposals/default-values-for-properties-in-interfaces.md b/proposals/default-values-for-properties-in-interfaces.md new file mode 100644 index 000000000..68b917ea1 --- /dev/null +++ b/proposals/default-values-for-properties-in-interfaces.md @@ -0,0 +1,95 @@ +# Default values for properties in interfaces + +* **Type**: Design proposal +* **Author**: Marcin Moskała +* **Status**: Under consideration + +Discussion of this proposal is held in [this issue](https://github.com/Kotlin/KEEP/issues/XXX). + +## Proposition + +Just as we can have default bodies for methods in interfaces, we should be able to give default values for properties: + +``` +interface MyTrait { + + var items: List = emptyList() + + fun isEmpty(): Boolean { + return items.isEmpty() + } +} +``` + +Under the hood it can work just as default bodies for methods - compiler can use default property if property is not overriten. + +## Use cases + +This would be useful besically everywhere we want to design in objective way. With that, more features can be implemented as a traits instead of as a classes. We can also implement multiple interfaces and extend only single class, so this gives us a lot of possibilities for using implementation to add some behavior. + +Simple example is `BasePresenter`. Let's say we needs to collect jobs to cancel them when presenter is destroyed. We can implement it is an abstract class: + +``` +abstract class JobsHandlingPresenter : Presenter { + + var jobs: List = emptyList() + + override fun onDestroy() { + jobs.forEach { it.cancel() } + } +} +``` + +Now let's say we need to add some other behavior using inheritance to some of presenters. Let's say we want to handle dialogs: (yes, it should be in Activity in MVP) + +``` +abstract class DialogHandlingPresenter : Presenter { + + var dialogs: List = emptyList() + + override fun onDestroy() { + dialogs.filter { it.isShowing }.forEach { it.cancel() } + } +} +``` + +Although if we extend `JobsHandlingPresenter` then we cannot extend `BasePresenter`. The common outcome is that we end up with `BasePresenter` that has all this responsibilities: +``` +abstract class JobsHandlingPresenter : Presenter { + + var jobs: List = emptyList() + var dialogs: List = emptyList() + + override fun onDestroy() { + jobs.forEach { it.cancel() } + dialogs.filter { it.isShowing }.forEach { it.cancel() } + } +} +``` + +Althoug this is not a good pattern because: + * It does have much more then single responsibility + * It provides responsibilities to classes they don't need + +Simple solution would be to allow default values for properties in interfaces. Then we would define this responsibilities as separate interfaces and every presenter would use only traits it needs: +``` +interface JobsHandlingPresenter : Presenter { + + var jobs: List = emptyList() + var dialogs: List = emptyList() + + override fun onDestroy() { + jobs.forEach { it.cancel() } + dialogs.filter { it.isShowing }.forEach { it.cancel() } + } +} + +interface DialogHandlingPresenter : Presenter { + + var dialogs: List = emptyList() + + override fun onDestroy() { + dialogs.filter { it.isShowing }.forEach { it.cancel() } + } +} +``` From d697d8b386b863604be516e64e2f83449ea2237a Mon Sep 17 00:00:00 2001 From: Marcin Moskala Date: Tue, 27 Mar 2018 00:10:07 +0200 Subject: [PATCH 2/3] Correction --- proposals/default-values-for-properties-in-interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/default-values-for-properties-in-interfaces.md b/proposals/default-values-for-properties-in-interfaces.md index 68b917ea1..735027557 100644 --- a/proposals/default-values-for-properties-in-interfaces.md +++ b/proposals/default-values-for-properties-in-interfaces.md @@ -21,7 +21,7 @@ interface MyTrait { } ``` -Under the hood it can work just as default bodies for methods - compiler can use default property if property is not overriten. +Under the hood it can work just as default bodies for methods - the compiler can use default property if the property is not overridden. ## Use cases From 1c538673ecfd6a98fe6e785dcb7639c33da0bd5d Mon Sep 17 00:00:00 2001 From: Marcin Moskala Date: Tue, 27 Mar 2018 00:10:47 +0200 Subject: [PATCH 3/3] Correct link --- proposals/default-values-for-properties-in-interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/default-values-for-properties-in-interfaces.md b/proposals/default-values-for-properties-in-interfaces.md index 735027557..92db285da 100644 --- a/proposals/default-values-for-properties-in-interfaces.md +++ b/proposals/default-values-for-properties-in-interfaces.md @@ -4,7 +4,7 @@ * **Author**: Marcin Moskała * **Status**: Under consideration -Discussion of this proposal is held in [this issue](https://github.com/Kotlin/KEEP/issues/XXX). +Discussion of this proposal is held in [this issue](https://github.com/Kotlin/KEEP/pull/102). ## Proposition