Created
October 28, 2016 10:41
-
-
Save patterns/cef6f4f1608ac4a38227870dd08f00dd to your computer and use it in GitHub Desktop.
Microsvc to compare adapters vs facade
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
# | |
# examples of calling svc from CLI | |
./hash | |
curl -XPOST -d'{"s":"hello"}' localhost:8080/hash | |
./hash -algo=SHA256 | |
curl -XPOST -d'{"s":"hello"}' localhost:8080/hash | |
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
package main | |
import ( | |
"crypto/sha1" | |
"crypto/sha256" | |
"fmt" | |
) | |
// Adaptee is the existing functionality. | |
type Adaptee struct { | |
Salt []byte | |
} | |
// Hash is calculated with Salt prefixed | |
func (a Adaptee) Hash(data []byte) []byte { | |
sz := len(data) + len(a.Salt) | |
t := make([]byte, sz) | |
copy(t, a.Salt) | |
copy(t[len(a.Salt):], data) | |
h := sha1.New() | |
h.Write(t) | |
digest := h.Sum(nil) | |
return digest | |
} | |
// Target is the *new* contract | |
type Target struct { | |
Salt []byte | |
Algo string | |
} | |
// Facade to hide ctors behind a *static* | |
type Facade struct {} | |
func (t Target) GetDigest(data string) string { | |
sz := len(data) + len(t.Salt) | |
d := make([]byte, sz) | |
copy(d, t.Salt) | |
copy(d[len(t.Salt):], []byte(data)) | |
h := sha256.New() | |
h.Write(d) | |
digest := h.Sum(nil) | |
return fmt.Sprintf("%x", digest) | |
} | |
// Adapter is the part that sits in between the Client and Target/Adaptee | |
type Adapter struct { | |
Salt []byte | |
Algo string | |
Calc func(string) string | |
} | |
// New is a ctor to handle Target approach | |
func (Adapter) New2(target Target) Adapter { | |
a := Adapter{target.Salt, target.Algo, target.GetDigest} | |
return a | |
} | |
// New is a ctor to bridge the original Adaptee | |
func (Adapter) New(target Adaptee) Adapter { | |
a := Adapter{ | |
target.Salt, | |
"SHA1", | |
func(data string) string { | |
d := target.Hash([]byte(data)) | |
return fmt.Sprintf("%x", d) | |
}, | |
} | |
return a | |
} | |
// Calc is a convenience method to simplify the diff options | |
func (Facade) Calc(data string) string { | |
var a Adapter | |
if algorithm == "SHA256" { | |
// Adapter2 configured to SHA256 | |
a = Adapter{}.New2(Target{[]byte(salt), "SHA256"}) | |
} else { | |
// Adapter1 configured to original SHA1 | |
a = Adapter{}.New(Adaptee{[]byte(salt)}) | |
} | |
return a.Calc(data) | |
} |
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
package main | |
import ( | |
"flag" | |
"encoding/json" | |
"errors" | |
"log" | |
"net/http" | |
"golang.org/x/net/context" | |
"github.com/go-kit/kit/endpoint" | |
httptransport "github.com/go-kit/kit/transport/http" | |
) | |
// StringService provides operations on strings. | |
type StringService interface { | |
Hash(string) (string, error) | |
} | |
type stringService struct{} | |
func (stringService) Hash(s string) (string, error) { | |
if s == "" { | |
return "", ErrEmpty | |
} | |
// Adapter1 configured to original SHA1 | |
//adapter1 := Adapter{}.New(Adaptee{[]byte(salt)}) | |
//d1 := adapter1.Calc(s) | |
d1 := Facade{}.Calc(s) | |
return d1, nil | |
} | |
var salt string | |
var algorithm string | |
func init() { | |
flag.StringVar(&salt, "salt", "seedXYZ", "Salt value to prefix") | |
flag.StringVar(&algorithm, "algo", "SHA1", "Algorithm of hash") | |
flag.Parse() | |
} | |
func main() { | |
ctx := context.Background() | |
svc := stringService{} | |
hashHandler := httptransport.NewServer( | |
ctx, | |
makeHashEndpoint(svc), | |
decodeHashRequest, | |
encodeResponse, | |
) | |
http.Handle("/hash", hashHandler) | |
log.Fatal(http.ListenAndServe(":8080", nil)) | |
} | |
func makeHashEndpoint(svc StringService) endpoint.Endpoint { | |
return func(ctx context.Context, request interface{}) (interface{}, error) { | |
req := request.(hashRequest) | |
v, err := svc.Hash(req.S) | |
if err != nil { | |
return hashResponse{v, err.Error()}, nil | |
} | |
return hashResponse{v, ""}, nil | |
} | |
} | |
func decodeHashRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
var request hashRequest | |
if err := json.NewDecoder(r.Body).Decode(&request); err != nil { | |
return nil, err | |
} | |
return request, nil | |
} | |
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { | |
return json.NewEncoder(w).Encode(response) | |
} | |
type hashRequest struct { | |
S string `json:"s"` | |
} | |
type hashResponse struct { | |
V string `json:"v"` | |
Err string `json:"err,omitempty"` // errors don't define JSON marshaling | |
} | |
// ErrEmpty is returned when an input string is empty. | |
var ErrEmpty = errors.New("empty string") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment