-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
4 changed files
with
185 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |