Decorator 패턴
•
Dacorator는 프로그래밍 패턴중에 하나이다.
•
데코레이터는 꾸미기라고 볼수 있다.
•
예를 들면 공책이 있으면 그 위에 꽃을 붙여넣은것 처럼 글씨를 작성하는 원래의 기능과는 상관 없는 부가기능을 넣는 행위를 데코레이터라고 한다.
•
원래 기능을 그대로 두고 부가 기능을 생성하는 방법
•
데이터를 보내는 상황을 말해보자
•
부가기능으로 데이터를 보낼때에 압축을 해서 보낼수 있고
•
부가기능으로 암호화로 해서 보낼수도 있고
•
로그 남기기, 마케팅 서버 측에 알림을 보내기 등등 부가기능을 많이 만들수 있다.
•
이러한 추가된 부분을 데코레이터라고 한다.
•
이러한 부가 기능을 무작정 하나로 묶어버릴때 문제점이 발생할 수 있는데,
•
문제점은 부가 기능 특성상 자주 바뀔 수 있다는 점이다.
•
압축을 예를들면 zip 파일로 변경하고 싶을때에나, 암호화 방식을 마음대로 변경할 수 있다.
•
그러면 어떻게 구현을 할까?
•
즉 구현을 하는 방법이 바로 Design Pattern 이다.
•
디자인 아키텍처가 엄청 중요하다.
•
디자인 패턴 서적 추천
•
디자인 패턴 방식중에 하나가 Decorator 패턴이다.
/main.go
package main
import (
"fmt"
"github.com/GloryKim/goWeb/web9/lzw"
"github.com/GloryKim/goWeb/web9/cipher"
)
type Component interface { // 인터페이스는 컨포넌트라고 명명한다.
Operator(string) //인자를 스트링으로 받는 오퍼레이터가 있다.
}
var sentData string // 전역 변수 설정
var recvData string
type SendComponent struct {} // 컨포넌트를 보내는 용도
func (self *SendComponent) Operator(data string) {
// Operator가 데이터를 전송한다.
sentData = data
}
type ZipComponent struct { //압축하는 컴포넌트가 있다.
com Component
}
func (self *ZipComponent) Operator(data string) {
zipData, err := lzw.Write([]byte(data)) //lzw 이건 압축하는 함수이다. 출처 : https://www.youtube.com/watch?v=zK2gvkagD-w&list=PLy-g2fnSzUTDALoERcKDniql16SAaQYHF&index=9
if err != nil {// 문제가 생기면 Panic이 발생
panic(err)
}
self.com.Operator(string(zipData)) //압축한 데이터로 생성
}
type EncryptComponent struct {// 암호화 컴포넌트 //cipher 같은경우에도 위의 사이트에서 제공한다.
key string
com Component
}
func (self *EncryptComponent) Operator(data string) {
encryptData, err := cipher.Encrypt([]byte(data), self.key)
if err != nil {
panic(err)
}
self.com.Operator(string(encryptData))
}
type DecryptComponent struct {
key string //암호화에는 Key 값도 있어야하기에 선언한다.
com Component
}
func (self *DecryptComponent) Operator(data string) {//암호를 푸는 오퍼레이터
decryptData, err := cipher.Decrypt([]byte(data), self.key)
if err != nil {
panic(err)
}
self.com.Operator(string(decryptData))
}
type UnzipComponent struct {
com Component
}
func (self *UnzipComponent) Operator(data string) { // 여기서 왜 포인터를 썼을까?
unzipData, err := lzw.Read([]byte(data))
if err != nil {
panic(err)
}
self.com.Operator(string(unzipData)) //왜 제귀함수식으로 했을까?
}
type ReadComponent struct {}
func (self *ReadComponent) Operator(data string) {
recvData = data
}
func main() {
//암호화 하고 압축하고 센드한다.
/*
//구조체를 연동해서 왜 쓰는지???
sender := &EncryptComponent{key:"abcde",
com: &ZipComponent{
com: &SendComponent{}
}
}
*/
sender := &ZipComponent{
com: &SendComponent{},
}
sender.Operator("Hello World")
fmt.Println(sentData)
receiver := &UnzipComponent{
com: &ReadComponent{},
}
receiver.Operator(sentData)
fmt.Println(recvData)
}
Go
복사
/lzw/lzw.go
package lzw
import (
"bytes"
"compress/lzw"
"fmt"
"io/ioutil"
)
// Write zip the data using lzw
func Write(data []byte) ([]byte, error) {
buf := new(bytes.Buffer)
writer := lzw.NewWriter(buf, lzw.LSB, 8)
n, err := writer.Write(data)
if n != len(data) {
return nil, fmt.Errorf("Not enough write:%d dataSize:%d", n, len(data))
}
if err != nil {
return nil, err
}
writer.Close()
return buf.Bytes(), nil
}
// Read unzip the data using lzw
func Read(data []byte) ([]byte, error) {
r := bytes.NewReader(data)
reader := lzw.NewReader(r, lzw.LSB, 8)
origData, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
return origData, nil
}
Go
복사
/cipher/cipher.go
package cipher
import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"io"
)
func createHash(key string) string {
hasher := md5.New()
hasher.Write([]byte(key))
return hex.EncodeToString(hasher.Sum(nil))
}
// Encrypt encrypt data using aes
func Encrypt(data []byte, passphrase string) ([]byte, error) {
block, err := aes.NewCipher([]byte(createHash(passphrase)))
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
ciphertext := gcm.Seal(nonce, nonce, data, nil)
return ciphertext, nil
}
// Decrypt decrypt data using aes
func Decrypt(data []byte, passphrase string) ([]byte, error) {
key := []byte(createHash(passphrase))
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
Go
복사