///
Search

09_Decorator

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
복사