Purpose of the pattern
Proxy is a wrapper for an object that is used behind the scenes. This means that it can provide additional logic for that object. Both the proxy and the object implement the same interface. It’s mostly used when additional access control is required.
Implementation
- Create Subject
interface
, which will be used by the clients. - Create RealSubject
class
, which implements Subject and is the default implementation. - Create Proxy
class
, which implements Subject and has a RealSubject value in it.
Both proxy
object and the real object
implement the same interface
. It’s mostly used with Services
.
What do we get from that?
- Managing
RealSubject
lifecycle without polluting clients’ code with it. Proxy
can work even if theRealSubject
is unavailable or throws an error.- You’re able to introduce multiple
Proxy
classes without having to change anything at all since the client doesn’t know whether it’s usingProxy
orRealSubject
thus complying with the Open/Closed principle. - Extension of
RealSubject
functionality. For example, you could add caching that is controlled for that specificRealSubject
.
Example
Your task is to create a Chat App. You’re provided with an API that gets and sends messages to do this. However, the chat needs to be secure. This means you’ll have to encode and decode the messages.
Job Offers
Let’s start by defining ChatService
interface:
interface ChatService {
fun sendMessage(message: String)
fun getMessage(): String
}
Now, we need to implement DefaultChatService
:
class DefaultChatService : ChatService {
override fun sendMessage(message: String) {
// Send message
}
override fun getMessage(): String {
// Get message
}
}
Finally, for a layer of additional security, we create ChatServiceSecureProxy
:
class ChatServiceSecureProxy(private val encoder: Encoder) : ChatService {
private val chatService: ChatService = DefaultChatService()
override fun sendMessage(message: String) {
chatService.sendMessage(encoder.encode(message))
}
override fun getMessage(): String {
val message = chatService.getMessage()
return encoder.decode(message)
}
}
Now, the encoding and decoding are encapsulated in a special class. If some of the messages needn’t to be secure, we’re still able to use the DefaultChatService
because both of them implement the ChatService
.
The pattern is similar to the Decorator, but the intents are different. Proxy manages the lifecycle on its own. While in Decorator, the client controls the composition of the objects and lifecycles.
Thanks for reading! Please clap if you learned something and follow me for more 🙂
More design patterns
Based on the book:
“Wzorce projektowe : elementy oprogramowania obiektowego wielokrotnego użytku” — Erich Gamma Autor; Janusz Jabłonowski (Translator); Grady Booch (Introduction author); Richard Helm (Author); Ralph Johnson (Author); John M Vlissides (Author)
This article is previously published on proandroiddev.com