-
Notifications
You must be signed in to change notification settings - Fork 1
Observer with subscription #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| package oop.Observer.subscription | ||
|
|
||
| class PublishSubject<A>(private val observers: MutableList<Observer<A>> = mutableListOf()) { | ||
|
|
||
| fun register(observer: Observer<A>): Observer<A> { | ||
| observers.add(observer) | ||
| return observer | ||
| } | ||
|
|
||
| fun notify(value: A) { | ||
| observers.forEach { it.update(value) } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| open class Observer<A>(private val value: A) { | ||
|
|
||
| private var subscription: Subscription<A>? = null | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need a Subscription class? 🤔
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to subscribe and unsuscribe
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the register method is in the publisher, you only need to add another method to remove subscriber |
||
|
|
||
| open fun update(value: A) { | ||
| if (subscription?.isSubscribed ?: false) { | ||
| subscription?.update?.invoke(value) | ||
| } | ||
| } | ||
|
|
||
| fun subscribe(success: (A) -> Unit, update: (A) -> Unit): Subscription<A> { | ||
| success(value) | ||
| return Subscription(update).also { subscription = it } | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| class Subscription<A> internal constructor(internal val update: (A) -> Unit, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Internal constructor??? what is that? 😲
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. internal is like in java a class without public, is a visibility package only.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is for testing purpose I suppose? |
||
| internal var isSubscribed: Boolean = true) { | ||
|
|
||
| fun unregister() { | ||
| isSubscribed = false | ||
| } | ||
|
|
||
| fun register() { | ||
| isSubscribed = true | ||
| } | ||
| } | ||
|
|
||
| fun main(args: Array<String>) { | ||
| val success: (String) -> Unit = { println("Value: $it") } | ||
|
|
||
| val subject: PublishSubject<String> = PublishSubject() | ||
|
|
||
| var firstSub = subject.register(Observer("First request a value and updates")) | ||
| .subscribe(success, { println("First updated: $it") }) | ||
| var secondSub = subject.register(Observer("Second request only updates")) | ||
| .subscribe({}, { println("Second updated: $it") }) | ||
| var thirdSub = subject.register(Observer("Third request a value")) | ||
| .subscribe(success, {}) | ||
|
|
||
| println() | ||
| println("Notify: Toni") | ||
| subject.notify("Toni"); println() | ||
| println("Notify: Cotel") | ||
| subject.notify("Cotel"); println() | ||
|
|
||
| println("Unregister all"); println() | ||
| firstSub.unregister() | ||
| secondSub.unregister() | ||
| thirdSub.unregister() | ||
|
|
||
| println("Notify: Fran") | ||
| subject.notify("Fran") | ||
| println("Nothing happens");println() | ||
|
|
||
| println("Register first and second"); println() | ||
| firstSub.register() | ||
| secondSub.register() | ||
|
|
||
| println("Notify: That's all") | ||
| subject.notify("That's all"); println() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,77 @@ | ||
| package oop.Proxy | ||
|
|
||
| interface Car { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a missed commit or it is included too?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a gost commit lol hahaha
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ghost* 👻 |
||
| fun isNovel(): Boolean | ||
| fun drive() | ||
| } | ||
|
|
||
| class CarProtectionProxy(private val realObject: Car) : Car { | ||
| override fun drive() { | ||
| if (!realObject.isNovel()) { | ||
| realObject.drive() | ||
| } else { | ||
| println("You can't drive a car") | ||
| } | ||
| } | ||
|
|
||
| override fun isNovel(): Boolean = realObject.isNovel() | ||
| } | ||
|
|
||
| class CarVirtualProxy(private val create: () -> Car) : Car { | ||
|
|
||
| private val realObject by lazy { | ||
| create() | ||
| } | ||
|
|
||
| override fun drive() = realObject.drive() | ||
|
|
||
| override fun isNovel(): Boolean = realObject.isNovel() | ||
| } | ||
|
|
||
| class CarSmartProxy(private val realObject: Car, | ||
| var accesDriver: Int = 0) : Car { | ||
|
|
||
| override fun drive() { | ||
| realObject.drive() | ||
| accesDriver++ | ||
| } | ||
|
|
||
| override fun isNovel(): Boolean = realObject.isNovel() | ||
| } | ||
|
|
||
| class RealCar(val name: String, val driverAge: Int) : Car { | ||
| override fun drive() = println("$name is driving a car") | ||
|
|
||
| override fun isNovel(): Boolean = driverAge < 18 | ||
| } | ||
|
|
||
| fun main(args: Array<String>) { | ||
|
|
||
|
|
||
| var carVirtualProxy = CarVirtualProxy({ RealCar("tonilopezmr", 5) }) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you wrap that constructor parameter in a lambda?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be lazy, do you remember?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I confused it with
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because Virtual is lazy |
||
| var carProtectionProxy = CarProtectionProxy(carVirtualProxy) | ||
| var carSmartProxy = CarSmartProxy(carVirtualProxy) | ||
|
|
||
| //Subject or Client usa el proxy | ||
|
|
||
| carProtectionProxy.drive() | ||
| carSmartProxy.drive() | ||
|
|
||
| println("Toni has driven ${carSmartProxy.accesDriver} times") | ||
|
|
||
|
|
||
|
|
||
|
|
||
| } | ||
|
|
||
| interface Payment { | ||
| fun pay(transaction: Transaction) | ||
| } | ||
|
|
||
| class PaymentProtectionProxy(private val realPayment: Payment) : Payment { | ||
|
|
||
| override fun pay(transaction: Transaction) { | ||
| if (!transaction.isInternational){ | ||
| if (!transaction.isInternational) { | ||
| realPayment.pay(transaction) | ||
| } else { | ||
| println("Can't pay international transactions") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| package oop.Observer.subscription | ||
|
|
||
| import io.kotlintest.matchers.shouldBe | ||
| import io.kotlintest.matchers.shouldEqual | ||
| import io.kotlintest.specs.WordSpec | ||
|
|
||
| class ObserverWithSubscription : WordSpec() { | ||
|
|
||
| init { | ||
| "SubjectPublish" should { | ||
| val observers = mutableListOf<Observer<String>>() | ||
| val subject: PublishSubject<String> = PublishSubject(observers) | ||
|
|
||
| "register a observer" { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow nice and klean 😏 I like it 👍 🔝
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I was starting with a JUnit test, but how I have created a one file for 3 classes, I prefer use this kind of test to separate differents things, like Subject and Observer 👍 |
||
| subject.register(Observer("hello world")) | ||
| observers.size shouldBe 1 | ||
| } | ||
|
|
||
| "register a observer and notify it" { | ||
| val observer = MockObserver("Hello world") | ||
| subject.register(observer) | ||
| subject.notify("Next") | ||
| observer.hasUpdateCalled shouldBe true | ||
| observer.value shouldBe "Next" | ||
| } | ||
|
|
||
| } | ||
|
|
||
| "Observable" should { | ||
|
|
||
| "call success on subscription" { | ||
| var hasCalled: Boolean = false | ||
| val observer = Observer("Hello world") | ||
| val success: (String) -> Unit = { value -> | ||
| hasCalled = true | ||
| value shouldBe "Hello world" | ||
| } | ||
| observer.subscribe(success, { 2 shouldBe 3 }) | ||
| hasCalled shouldBe true | ||
| } | ||
|
|
||
| "update value if is subscribed" { | ||
| var hasCalled: Boolean = false | ||
| val observer = Observer("Hello world") | ||
| val update: (String) -> Unit = { value -> | ||
| hasCalled = true | ||
| value shouldBe "Updated" | ||
| } | ||
| val subscription = observer.subscribe({}, update) | ||
| observer.update("Updated") | ||
| hasCalled shouldBe true | ||
| subscription.isSubscribed shouldBe true | ||
| } | ||
|
|
||
|
|
||
| "not update if is not subscribed" { | ||
| var hasCalled: Boolean = false | ||
| val observer = Observer("Hello world") | ||
| val update: (String) -> Unit = { hasCalled = true } | ||
| val subscription = observer.subscribe({}, update) | ||
| subscription.unregister() | ||
| observer.update("Updated") | ||
| hasCalled shouldBe false | ||
| subscription.isSubscribed shouldBe false | ||
| } | ||
|
|
||
| "update if was not subscribed but renew the subscription"{ | ||
| var hasCalled: Boolean = false | ||
| val observer = Observer("Hello world") | ||
| val update: (String) -> Unit = { value -> | ||
| hasCalled = true | ||
| value shouldBe "Updated" | ||
| } | ||
| val subscription = observer.subscribe({}, update) | ||
| subscription.unregister() | ||
| subscription.register() | ||
| observer.update("Updated") | ||
| hasCalled shouldBe true | ||
| subscription.isSubscribed shouldBe true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| class MockObserver(value: String) : Observer<String>(value) { | ||
|
|
||
| var hasUpdateCalled = false | ||
| var value: String = "" | ||
|
|
||
| override fun update(value: String) { | ||
| this.hasUpdateCalled = true | ||
| this.value = value | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
open class? You could have made it abstract or even an interface with implementation (I think Kotlin let to do that...)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, should exist a Observer interface, but for this example is more clear without a one component more.