Skip to content

Commit 0e7c998

Browse files
authored
Merge pull request #41 from CloudCoders/proxy-toni
Proxy
2 parents a8d3fe3 + 6bd21e3 commit 0e7c998

7 files changed

Lines changed: 366 additions & 3 deletions

File tree

README.md

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Kotlin OOP and FP Design Patterns
3232
* [x] [Decorator](#decorator)
3333
* [x] [Facade](#facade)
3434
* [ ] [Flyweight](#flyweight)
35-
* [ ] [Proxy](#proxy)
35+
* [x] [Proxy](#proxy)
3636
* FP
3737
* [Monads](#monads)
3838
* [x] [Option/Maybe](#option)
@@ -550,13 +550,141 @@ Flyweight
550550
551551
**In progress**
552552

553-
Proxy
553+
[Proxy](/src/main/kotlin/oop/Proxy)
554554
------
555555

556556
> It provides a placeholder for another object to control access to it.
557557
> It is an extra level of indirection to support controlled or intelligent access.
558558
559-
**In progress**
559+
There are 3 types of Proxies
560+
561+
* [Protection Proxy](#protection-proxy) - Which control the access to an object.
562+
* [Virtual Proxy](#virtual-proxy) - Which prevents creating an object until it is really necessary to save resources.
563+
* [Remote Proxy](#remote-proxy) - Which duty is to create a correct request to ask a remote real object which may not be in our domain.
564+
565+
**Protection proxy**
566+
567+
### Example
568+
569+
```kotlin
570+
class Transaction(val amount: Double,
571+
val isInternational: Boolean)
572+
573+
interface Payment {
574+
fun pay(transaction: Transaction)
575+
}
576+
577+
class PaymentProtectionProxy(private val payment: Payment): Payment {
578+
override fun pay(transaction: Transaction) {
579+
if (transaction.isInternational) println("Payment is international, we do not allow it")
580+
else payment.pay(transaction)
581+
}
582+
}
583+
584+
class RealPayment(val initialAmount: Double): Payment {
585+
override fun pay(transaction: Transaction) {
586+
initialAmount -= transaction.amount
587+
println("Successful!")
588+
}
589+
}
590+
```
591+
592+
### Usage
593+
594+
```kotlin
595+
val account = RealPayment(100.0)
596+
val localBank = PaymentProtectionProxy(account)
597+
598+
localBank.pay(Transaction(3.15, true)) // It won't allow us to pay
599+
localBank.pay(Transaction(3.15, false)) // It will work
600+
```
601+
602+
**Virtual Proxy**
603+
604+
We can use kotlin's [lazy delegation](https://kotlinlang.org/docs/reference/delegated-properties.html#lazy)
605+
606+
### Example
607+
608+
```kotlin
609+
interface Screen {
610+
fun show()
611+
}
612+
613+
class VirtualScreen(val screenCreation: () -> Screen) : Screen {
614+
private val realScreen: Screen by lazy {
615+
screenCreation()
616+
}
617+
618+
override fun show() {
619+
realScreen.show()
620+
}
621+
}
622+
623+
class RealScreen : Screen {
624+
constructor() {
625+
// It gets really hungry in here
626+
}
627+
628+
override fun show() {
629+
println("¯\_(ツ)_/¯")
630+
}
631+
}
632+
```
633+
634+
### Usage
635+
636+
```kotlin
637+
val virtualScreen = VirtualScreen({ RealScreen() }) // We haven't created a RealScreen yet
638+
639+
// Several lines after...
640+
641+
virtualScreen.show() // RealScreen is needed now, so we create it
642+
643+
```
644+
645+
**Remote Proxy**
646+
647+
### Example
648+
649+
```kotlin
650+
// Client code
651+
652+
interface Message {
653+
fun writeInChannel(text: String, channel: String)
654+
}
655+
656+
class MessageProxy(private val outpuStream: OutputStream) : Message {
657+
override fun writeInChannel(text: String, channel: String) {
658+
outpuStream.write("Headers:$text:$channel:Goodbye")
659+
}
660+
}
661+
662+
// Server code
663+
664+
class ServerMessage(val channels: List<Channel>) : Message {
665+
override fun writeInChannel(text: String, channel: String) {
666+
channels.forEach {
667+
if (it == channel) {
668+
println("$text")
669+
}
670+
}
671+
}
672+
}
673+
674+
class Server {
675+
val channels = listOf("1", "2", "3")
676+
val messageHandler = ServerMessage(channels)
677+
678+
fun onClientMessage(request: String) {
679+
val (message, channel) = request
680+
.removePrefix("Headers:")
681+
.removeSuffix(":Goodbye")
682+
.split(":")
683+
684+
messageHandler.writeInChannel(message, channel)
685+
}
686+
}
687+
```
560688

561689
## Functional paradigm
562690

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package oop.Proxy
2+
3+
interface Payment {
4+
fun pay(transaction: Transaction)
5+
}
6+
7+
class PaymentProtectionProxy(private val realPayment: Payment) : Payment {
8+
9+
override fun pay(transaction: Transaction) {
10+
if (!transaction.isInternational){
11+
realPayment.pay(transaction)
12+
} else {
13+
println("Can't pay international transactions")
14+
}
15+
}
16+
}
17+
18+
class RealPayment(var amount: Double) : Payment {
19+
20+
override fun pay(transaction: Transaction) {
21+
amount -= transaction.amount
22+
println("Real payment successful")
23+
}
24+
25+
}
26+
27+
class Transaction(val amount: Double,
28+
val isInternational: Boolean)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package oop.Proxy
2+
3+
/**
4+
* Remote Proxy[edit]
5+
In distributed object communication, a local object represents a remote object (one that belongs to a different address space).
6+
The local object is a proxy for the remote object, and method invocation on the local object results in remote method invocation on the remote object.
7+
An example would be an ATM implementation, where the ATM might hold proxy objects for bank information that exists in the remote server.
8+
*
9+
* See https://en.wikipedia.org/wiki/Distributed_object_communication
10+
*/
11+
12+
interface Message {
13+
fun writeInChannel(message: String, channel: String)
14+
}
15+
16+
class MessageRemoteProxy(private val ouputStream: SimulateOutputStream) : Message {
17+
18+
private val addHeadTCP = ":TCPInformationLOL:"
19+
private val tailTCP = ":TCPendlol:"
20+
21+
override fun writeInChannel(message: String, channel: String) {
22+
ouputStream.write(addHeadTCP + message + ":" + channel + tailTCP)
23+
ouputStream.close()
24+
}
25+
}
26+
27+
class MessageServerObject(private val serverDomain: ServerDomain = ServerDomain()) : Message {
28+
29+
override fun writeInChannel(message: String, channel: String) {
30+
serverDomain.channels.forEach {
31+
if (it == channel){
32+
println("Message in $channel: $message")
33+
}
34+
}
35+
}
36+
37+
}
38+
39+
class ServerDomain(val channels: MutableList<String> = mutableListOf())
40+
41+
class SimulateOutputStream(private val networkSimulationMessageServerObject: Message) {
42+
43+
fun write(string: String) {
44+
println("write message on network: $string")
45+
46+
//when you sends the information another remote API,
47+
// grab this information and transform to remote object in his machine
48+
49+
//IN MY SERVER
50+
51+
var (message, channel) = string
52+
.removePrefix(":TCPInformationLOL:")
53+
.removeSuffix(":TCPendlol:")
54+
.split(":")
55+
56+
networkSimulationMessageServerObject.writeInChannel(message, channel)
57+
58+
//MY SERVER LEFT
59+
}
60+
61+
fun close() {}
62+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package oop.Proxy
2+
3+
interface Screen {
4+
fun show()
5+
}
6+
7+
class ScreenVirtualProxy(val createRealScreen: () -> Screen) : Screen {
8+
9+
val realScreen: Screen by lazy {
10+
createRealScreen()
11+
}
12+
13+
override fun show() {
14+
realScreen.show()
15+
}
16+
}
17+
18+
class RealScreen : Screen {
19+
constructor() {
20+
//init wtf recurses like chrome
21+
}
22+
23+
override fun show() {
24+
println(" )_(o.O)_( ")
25+
}
26+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package oop.Proxy
2+
3+
import org.hamcrest.core.Is.`is`
4+
import org.junit.Assert.assertThat
5+
import org.junit.Test
6+
7+
/**
8+
* Test of my implementation example, nothing related for object proxy
9+
*/
10+
class MessageRemoteProxyTest {
11+
12+
@Test
13+
fun `write correct message and channel in server`() {
14+
val networkSimulationMessageServerObject = MessageServerObjectMock()
15+
val outputStream = SimulateOutputStream(networkSimulationMessageServerObject)
16+
val messageRemoteProxy = MessageRemoteProxy(outputStream)
17+
18+
messageRemoteProxy.writeInChannel("Hello World!", "myChannel")
19+
20+
assertThat(networkSimulationMessageServerObject.message, `is`("Hello World!"))
21+
assertThat(networkSimulationMessageServerObject.channel, `is`("myChannel"))
22+
}
23+
24+
25+
class MessageServerObjectMock(var message: String = "",
26+
var channel: String = "") : Message {
27+
28+
override fun writeInChannel(message: String, channel: String) {
29+
this.message = message
30+
this.channel = channel
31+
}
32+
}
33+
34+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package oop.Proxy
2+
3+
import junit.framework.Assert.assertFalse
4+
import junit.framework.Assert.assertTrue
5+
import org.junit.Test
6+
7+
8+
class PaymentProtectionProxyTest {
9+
10+
@Test
11+
fun `pay transaction success`() {
12+
var realPayment = RealPaymentMock()
13+
val protectionProxy = PaymentProtectionProxy(realPayment)
14+
15+
protectionProxy.pay(Transaction(100.0, false))
16+
17+
assertTrue(realPayment.callPay)
18+
}
19+
20+
@Test
21+
fun `pay transaction failure if international`() {
22+
var realPayment = RealPaymentMock()
23+
val protectionProxy = PaymentProtectionProxy(realPayment)
24+
25+
protectionProxy.pay(Transaction(100.0, true))
26+
27+
assertFalse(realPayment.callPay)
28+
}
29+
30+
class RealPaymentMock(var callPay: Boolean = false) : Payment {
31+
32+
override fun pay(transaction: Transaction) {
33+
callPay = true
34+
}
35+
36+
}
37+
38+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package oop.Proxy
2+
3+
import org.hamcrest.core.Is.`is`
4+
import org.junit.Assert.*
5+
import org.junit.Test
6+
7+
class ScreenVirtualProxyTest {
8+
9+
@Test
10+
fun `not create real object in proxy construction`() {
11+
var createCount = false
12+
var createRealScreen = {
13+
createCount = true
14+
RealScreen()
15+
}
16+
17+
val screenVirtualProxy = ScreenVirtualProxy(createRealScreen)
18+
19+
assertFalse(createCount)
20+
}
21+
22+
@Test
23+
fun `create real object when show screen`() {
24+
val screenVirtualProxy = ScreenVirtualProxy({ RealScreen() })
25+
26+
screenVirtualProxy.show()
27+
28+
assertNotNull(screenVirtualProxy.realScreen)
29+
}
30+
31+
@Test
32+
fun `create real object once when show screen`() {
33+
var createCount = 0
34+
35+
val screenVirtualProxy = ScreenVirtualProxy({
36+
createCount++
37+
RealScreen()
38+
})
39+
40+
screenVirtualProxy.show()
41+
screenVirtualProxy.show()
42+
43+
44+
assertThat(createCount, `is`(1))
45+
}
46+
47+
}

0 commit comments

Comments
 (0)