mirror of
https://github.com/tmrts/go-patterns.git
synced 2024-11-21 20:46:08 +03:00
structural/proxy: implement structural proxy pattern (#27)
* Update gitignore added JetBrains, LiteIDE and other exclude files * Added example of proxy realisation * Update proxy description with simple example * Update showcase with description, small refactore of code * Update proxy doc * Added comments in example proxy also added link to go play sandbox * Small improvement of proxy example * Update link for play golang * Corrected mistakes, splited user validation in proxy * Updated link to play golang and some mistakes
This commit is contained in:
parent
67efe3e622
commit
f978e42036
14
.gitignore
vendored
14
.gitignore
vendored
@ -19,6 +19,16 @@ _cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# JetBrains project files
|
||||
.idea/
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
@ -30,7 +30,7 @@ A curated collection of idiomatic design & application patterns for Go language.
|
||||
| [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | ✔ |
|
||||
| [Facade](/structural/facade.md) | Uses one type as an API to a number of others | ✘ |
|
||||
| [Flyweight](/structural/flyweight.md) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✘ |
|
||||
| [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✘ |
|
||||
| [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✔ |
|
||||
|
||||
## Behavioral Patterns
|
||||
|
||||
|
45
structural/proxy.md
Normal file
45
structural/proxy.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Proxy Pattern
|
||||
|
||||
The [proxy pattern](https://en.wikipedia.org/wiki/Proxy_pattern) provides an object that controls access to another object, intercepting all calls.
|
||||
|
||||
## Implementation
|
||||
|
||||
The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.
|
||||
|
||||
Short idea of implementation:
|
||||
```go
|
||||
// To use proxy and to object they must implement same methods
|
||||
type IObject interface {
|
||||
ObjDo(action string)
|
||||
}
|
||||
|
||||
// Object represents real objects which proxy will delegate data
|
||||
type Object struct {
|
||||
action string
|
||||
}
|
||||
|
||||
// ObjDo implements IObject interface and handel's all logic
|
||||
func (obj *Object) ObjDo(action string) {
|
||||
// Action behavior
|
||||
fmt.Printf("I can, %s", action)
|
||||
}
|
||||
|
||||
// ProxyObject represents proxy object with intercepts actions
|
||||
type ProxyObject struct {
|
||||
object *Object
|
||||
}
|
||||
|
||||
// ObjDo are implemented IObject and intercept action before send in real Object
|
||||
func (p *ProxyObject) ObjDo(action string) {
|
||||
if p.object == nil {
|
||||
p.object = new(Object)
|
||||
}
|
||||
if action == "Run" {
|
||||
p.object.ObjDo(action) // Prints: I can, Run
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
More complex usage of proxy as example: User creates "Terminal" authorizes and PROXY send execution command to real Terminal object
|
||||
See [proxy/main.go](proxy/main.go) or [view in the Playground](https://play.golang.org/p/mnjKCMaOVE).
|
127
structural/proxy/main.go
Normal file
127
structural/proxy/main.go
Normal file
@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// For example:
|
||||
// we must a execute some command
|
||||
// so before that we must to create new terminal session
|
||||
// and provide our user name and command
|
||||
func main() {
|
||||
// Create new instance of Proxy terminal
|
||||
t, err := NewTerminal("gopher")
|
||||
if err != nil {
|
||||
// panic: User cant be empty
|
||||
// Or
|
||||
// panic: You (badUser) are not allowed to use terminal and execute commands
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
// Execute user command
|
||||
excResp, excErr := t.Execute("say_hi") // Proxy prints to STDOUT -> PROXY: Intercepted execution of user (gopher), asked command (say_hi)
|
||||
if excErr != nil {
|
||||
fmt.Printf("ERROR: %s\n", excErr.Error()) // Prints: ERROR: I know only how to execute commands: say_hi, man
|
||||
}
|
||||
|
||||
// Show execution response
|
||||
fmt.Println(excResp) // Prints: gopher@go_term$: Hi gopher
|
||||
}
|
||||
|
||||
/*
|
||||
From that it's can be different terminals realizations with different methods, propertys, yda yda...
|
||||
*/
|
||||
|
||||
// ITerminal is interface, it's a public method whose implemented in Terminal(Proxy) and Gopher Terminal
|
||||
type ITerminal interface {
|
||||
Execute(cmd string) (resp string, err error)
|
||||
}
|
||||
|
||||
// GopherTerminal for example:
|
||||
// Its a "huge" structure with different public methods
|
||||
type GopherTerminal struct {
|
||||
// user is a current authorized user
|
||||
User string
|
||||
}
|
||||
|
||||
// Execute just runs known commands for current authorized user
|
||||
func (gt *GopherTerminal) Execute(cmd string) (resp string, err error) {
|
||||
// Set "terminal" prefix for output
|
||||
prefix := fmt.Sprintf("%s@go_term$:", gt.User)
|
||||
|
||||
// Execute some asked commands if we know them
|
||||
switch cmd {
|
||||
case "say_hi":
|
||||
resp = fmt.Sprintf("%s Hi %s", prefix, gt.User)
|
||||
case "man":
|
||||
resp = fmt.Sprintf("%s Visit 'https://golang.org/doc/' for Golang documentation", prefix)
|
||||
default:
|
||||
err = fmt.Errorf("%s Unknown command", prefix)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
And now we will create owr proxy to deliver user and commands to specific objects
|
||||
*/
|
||||
|
||||
// Terminal is a implementation of Proxy, it's validates and sends data to GopherTerminal
|
||||
// As example before send commands, user must be authorized
|
||||
type Terminal struct {
|
||||
currentUser string
|
||||
gopherTerminal *GopherTerminal
|
||||
}
|
||||
|
||||
// NewTerminal creates new instance of terminal
|
||||
func NewTerminal(user string) (t *Terminal, err error) {
|
||||
// Check user if given correctly
|
||||
if user == "" {
|
||||
err = fmt.Errorf("User cant be empty")
|
||||
return
|
||||
}
|
||||
|
||||
// Before we execute user commands, we validate current user, if he have rights to do it
|
||||
if authErr := authorizeUser(user); authErr != nil {
|
||||
err = fmt.Errorf("You (%s) are not allowed to use terminal and execute commands", user)
|
||||
return
|
||||
}
|
||||
|
||||
// Create new instance of terminal and set valid user
|
||||
t = &Terminal{currentUser: user}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Execute intercepts execution of command, implements authorizing user, validates it and
|
||||
// poxing command to real terminal (gopherTerminal) method
|
||||
func (t *Terminal) Execute(command string) (resp string, err error) {
|
||||
// If user allowed to execute send commands then, for example we can decide which terminal can be used, remote or local etc..
|
||||
// but for example we just creating new instance of terminal,
|
||||
// set current user and send user command to execution in terminal
|
||||
t.gopherTerminal = &GopherTerminal{User: t.currentUser}
|
||||
|
||||
// For example our proxy can log or output intercepted execution... etc
|
||||
fmt.Printf("PROXY: Intercepted execution of user (%s), asked command (%s)\n", t.currentUser, command)
|
||||
|
||||
// Transfer data to original object and execute command
|
||||
if resp, err = t.gopherTerminal.Execute(command); err != nil {
|
||||
err = fmt.Errorf("I know only how to execute commands: say_hi, man")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// authorize validates user right to execute commands
|
||||
func authorizeUser(user string) (err error) {
|
||||
// As we use terminal like proxy, then
|
||||
// we will intercept user name to validate if it's allowed to execute commands
|
||||
if user != "gopher" {
|
||||
// Do some logs, notifications etc...
|
||||
err = fmt.Errorf("User %s in black list", user)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue
Block a user