Proxy/Wrapper Pattern
Proxy Design Pattern is a structural design pattern. This pattern suggests providing an extra layer of indirection for controlled and intelligent access to the main object.
In this pattern, a new proxy class is created that implements the same interface as the main object. This lets you execute some behavior before the actual logic of the main object. Let’s understand it more with an example:
A web server such as Nginx can act as a proxy for your application server. It provides
- Controlled access to your application server – For example, it can do rate limiting
- Additional behavior – For example, it can do some caching
See https://swsmile.info/post/design-pattern-proxy/ for Proxy Pattern.
Demo
A web server such as Nginx can act as a proxy for your application server:
- It provides controlled access to your application server.
- It can do rate limiting.
- It can do request caching.
server.go: Subject
package main
type server interface {
handleRequest(string, string) (int, string)
}
nginx.go: Proxy
package main
type nginx struct {
application *application
maxAllowedRequest int
rateLimiter map[string]int
}
func newNginxServer() *nginx {
return &nginx{
application: &application{},
maxAllowedRequest: 2,
rateLimiter: make(map[string]int),
}
}
func (n *nginx) handleRequest(url, method string) (int, string) {
allowed := n.checkRateLimiting(url)
if !allowed {
return 403, "Not Allowed"
}
return n.application.handleRequest(url, method)
}
func (n *nginx) checkRateLimiting(url string) bool {
if n.rateLimiter[url] == 0 {
n.rateLimiter[url] = 1
}
if n.rateLimiter[url] > n.maxAllowedRequest {
return false
}
n.rateLimiter[url] = n.rateLimiter[url] + 1
return true
}
application.go: Real subject
package main
type application struct {
}
func (a *application) handleRequest(url, method string) (int, string) {
if url == "/app/status" && method == "GET" {
return 200, "Ok"
}
if url == "/create/user" && method == "POST" {
return 201, "User Created"
}
return 404, "Not Ok"
}
main.go: Client code
package main
import "fmt"
func main() {
nginxServer := newNginxServer()
appStatusURL := "/app/status"
createuserURL := "/create/user"
httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
}
output.txt: Execution result
Url: /app/status
HttpCode: 200
Body: Ok
Url: /app/status
HttpCode: 200
Body: Ok
Url: /app/status
HttpCode: 403
Body: Not Allowed
Url: /app/status
HttpCode: 201
Body: User Created
Url: /app/status
HttpCode: 404
Body: Not Ok
Reference
- https://refactoring.guru/design-patterns/proxy/go/example#example-0
- https://golangbyexample.com/proxy-design-pattern-in-golang/