Skip to content

Commit a8d3fe3

Browse files
authored
Merge pull request #37 from CloudCoders/observer-pattern
Added Observer pattern
2 parents c670f0e + ce606a8 commit a8d3fe3

6 files changed

Lines changed: 101 additions & 5 deletions

File tree

README.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Kotlin OOP and FP Design Patterns
1414
* [ ] [Mediator](#mediator)
1515
* [ ] [Memento](#memento)
1616
* [ ] [Null Object](#null-object)
17-
* [ ] [Observer](#observer)
17+
* [x] [Observer](#observer)
1818
* [x] [State](#state)
1919
* [ ] [Template](#template)
2020
* [ ] [Visitor](#visitor)
@@ -189,12 +189,40 @@ Null Object
189189
190190
**In progress**
191191

192-
Observer
192+
[Observer](/src/main/kotlin/oop/Observer)
193193
------------
194194

195195
> It defines a _one-to-many_ dependency between object so that when one changes its state, all its dependents are notified and updated automatically.
196196
197-
**In progress**
197+
In Kotlin, this pattern is extremely easy to implement thanks to [property delegation](https://kotlinlang.org/docs/reference/delegated-properties.html)
198+
199+
### Example
200+
201+
```kotlin
202+
interface Observer<in T> {
203+
fun onValueChange(newValue: T, oldValue: T)
204+
}
205+
206+
class CustomersObserver : Observer<Int> {
207+
override fun onValueChange(newValue: Int, oldValue: Int) = when {
208+
newValue > oldValue -> println("A new customer entered. Current customers $newValue")
209+
else -> println("A customer left. Current customers: $newValue")
210+
}
211+
}
212+
213+
class Shop(private val observer: Observer<Int>) {
214+
var currentCustomers by Delegates.observable(0) { _, old, new ->
215+
observer.onValueChange(new, old)
216+
}
217+
}
218+
```
219+
220+
### Usage
221+
222+
```kotlin
223+
shop.currentCustomers++ // prints "A new customer entered ..."
224+
shop.currentCustomers-- // prints "A customer left ..."
225+
```
198226

199227
[State](/src/main/kotlin/oop/State)
200228
-------
@@ -451,7 +479,7 @@ kitchen.cook()
451479

452480
> It attachs additional responsabilities to an object dynamically.
453481
454-
In Kotlin, we don't need to redefine the methods of the decorated interface. We can use `by` to delegate those methods to the decorated class.
482+
In Kotlin, we don't need to redefine the methods of the decorated interface. We can use `by` to [delegate](https://kotlinlang.org/docs/reference/delegation.html) those methods to the decorated class.
455483

456484
### Example
457485

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ group 'com.cloudcoders'
22
version '1.0-SNAPSHOT'
33

44
buildscript {
5-
ext.kotlin_version = '1.1.0'
5+
ext.kotlin_version = '1.1.1'
66

77
repositories {
88
mavenCentral()
@@ -24,4 +24,5 @@ repositories {
2424
dependencies {
2525
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
2626
testCompile group: 'junit', name: 'junit', version: '4.11'
27+
testCompile "com.natpryce:hamkrest:1.4.0.0"
2728
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package oop.Observer
2+
3+
4+
class CustomersObserver : Observer<Int> {
5+
6+
override fun onValueChange(newValue: Int, oldValue: Int) = when {
7+
newValue > oldValue -> println("A new customer entered. Current customers: $newValue")
8+
else -> println("A customer left. Current customers: $newValue")
9+
}
10+
11+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package oop.Observer
2+
3+
4+
interface Observer<in T> {
5+
fun onValueChange(newValue: T, oldValue: T)
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package oop.Observer
2+
3+
import kotlin.properties.Delegates
4+
5+
class Shop(private val customersObserver : CustomersObserver) {
6+
7+
private var currentCustomers by Delegates.observable(0) { _, old, new ->
8+
customersObserver.onValueChange(new, old)
9+
}
10+
11+
fun customerEnter() = currentCustomers++
12+
13+
fun customerLeave() = currentCustomers--
14+
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package oop.Observer
2+
3+
import com.natpryce.hamkrest.Matcher
4+
import com.natpryce.hamkrest.assertion.assert
5+
import org.junit.Test
6+
import kotlin.properties.Delegates
7+
8+
9+
class ObserverTest {
10+
11+
var observer = object : Observer<Int> {
12+
override fun onValueChange(newValue: Int, oldValue: Int) {
13+
timesChanged++
14+
}
15+
}
16+
17+
var counter by Delegates.observable(0) { _, old, new ->
18+
observer.onValueChange(new, old)
19+
}
20+
21+
var timesChanged = 0
22+
23+
val `is` = { number: Int -> Matcher(Int::equals, number) }
24+
25+
@Test
26+
internal fun `observer should increment timesChanged on change`() {
27+
assert.that(timesChanged, `is`(0))
28+
29+
counter++
30+
assert.that(timesChanged, `is`(1))
31+
32+
counter--
33+
assert.that(timesChanged, `is`(2))
34+
}
35+
}

0 commit comments

Comments
 (0)