Go web 5. Decorater 패턴

JINSOO PARK·2021년 10월 27일
0

Go 로 만드는 웹

목록 보기
4/16
post-thumbnail

Decorater 패턴

객체의 결합을 통해 기능을 동적으로 유연하게 확장 할 수 있게 해주는 패턴

  • Component : 동적으로 추가된 기능을 가질 수 있는 객체에 대한 인터페이스를 정의한다.
  • ConcreteComponent : Component 인터페이스의 기능들을 실제로 구현한 객체
  • Decorator : Component 인터페이스를 따르는 객체를 참조할 수 있는 필드가 존재한다.
  • ConcreteDecorator: 구성 요소에 기능을 추가한다.

장점

  • 상속을 통한 하위 클래스를 만들지 않고도 객체의 기능을 확장할 수 있다.
  • 런타임에서 객체에 책임을 추가하고 제거할 수 있다.
  • 객체를 여러 데코레이터로 래핑하여 여러 동작을 합칠 수 있다.
  • 객체 지향 프로그래밍에서 중요한 단일 책임 원칙을 지킬 수 있다.

단점

  • 래퍼 스택에서 특정 래퍼를 제거하는 것은 어렵다.
  • 데코레이터 기능이 데코레이터 스택 순서에 의존해야 한다.
  • 코드가 복잡해 질 수 있다.
    링크텍스트



ex) 암호화와 압축

package main

import (
	"fmt"

	"github.com/tuckersGo/goWeb/web9/cipher"
	"github.com/tuckersGo/goWeb/web9/lzw"
)

type Component interface {
	Operator(string)
}

var sentData string

// 최종 결과 값

// 실제 기본기능을 가짐
type SendComponent struct{}

// 데이터를 보낸다.
func (self *SendComponent) Operator(data string) {
	sentData = data
}

// 압축하는 컴포넌트
type ZipComponent struct {
	com Component //기본 기능
}

// 압축 해주는 기능
func (self *ZipComponent) Operator(data string) {
	zipData, err := lzw.Write([]byte(data))
	// 터커의 압축 패키지
	if err != nil {
		panic(err) // 에러가 있을 시 프로그램 종료
	}
	self.com.Operator(string(zipData))
	//압축된 데이터가 샌드 컴포넌트의 오퍼레이터로
}

// 암호화 하는 컴포넌트
type EncryptComponent struct {
	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))
	// 암호화된 데이터가 압축 컴포넌트의 오퍼레이터로
}

func main() {
	sender := &EncryptComponent{key: "abcde", // 암호화 컴포넌트가 압축 컴포넌트를 가짐
		com: &ZipComponent{ // 압축 컴포넌트가 샌드 컴포넌트를 가짐
			com: &SendComponent{}}}

	sender.Operator(("Hello World"))
	// 암호화 컴포넌트의 오퍼레이터 호출

	fmt.Println(sentData)
	// 최종 결과값 출력
}
---------------------------------
qV�ȅ��l��Z�P�D(�ܐp��&w#̜��(�D�x��bb���

ex) 암호화와 압출 풀기

package main

import (
	"fmt"

	"github.com/tuckersGo/goWeb/web9/cipher"
	"github.com/tuckersGo/goWeb/web9/lzw"
)

type Component interface {
	Operator(string)
}

var sentData string    // 암호화, 압축 결과 값
var receiveData string // 복호화, 압축 해제 결과 값

<생략>

// 복호화 컴포넌트
type DecryptComponent struct {
	key string
	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 ReadCompenent struct{}

func (self *ReadCompenent) Operator(data string) {
	receiveData = data
}

func main() {
	sender := &EncryptComponent{key: "abcde", // 암호화 컴포넌트가 압축 컴포넌트를 가짐
		com: &ZipComponent{ // 압축 컴포넌트가 샌드 컴포넌트를 가짐
			com: &SendComponent{},
		},
	}

	sender.Operator(("Hello World"))
	// 암호화 컴포넌트의 오퍼레이터 호출

	fmt.Println(sentData)
	// 최종 결과값 출력

	receiver := &UnzipComponent{ // 압축해제 컴포넌트가 복호화 컴포넌트를 가짐
		com: &DecryptComponent{ // 복호화 컴포넌트가 압축해제 컴포넌트를 가짐
			key: "abcde",
			com: &ReadCompenent{},
		},
	}

	receiver.Operator(sentData)
	// 암호화,압축 된 결과 값을 receiver의 operator로
	fmt.Println(receiveData)
	// 최종 결과값 출력
}
-------------------------------------
3ބ $�Xo���h��]�M�����)�D@+F�ˍz���&�B@
Hello World



Web Decorator Handler

웹 서버에서 데코레이터가 필요한 이유

웹 서버는 단순하게 클라이언트의 요청을 받으면 해당 데이터를 반환해주는 역할을 한다.
이 것을 기본 기능으로 보았을때 암호화, 압축, log 등은 부가 기능이 된다.
이 부가 기능들을 넣고 빼는 일이 자주 일어나기 때문에 기본 기능에 해당하는 부분에 부가 기능이 들어가 있으면, 웹 서버를 오픈한 상태에서 기본 기능 부분까지 다 수정해 주어야 한다.
때문에 데코레이터를 이용하여 각 각의 기능을 나누어 해당 부분만 수정하기 위함이다.


ex) app.go

package myapp

import (
	"fmt"
	"net/http"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello World")
}

// 기본페이지에 idexhandler 적용
func NewHandler() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/", indexHandler)
	return mux
}

ex) deco.go

package decoHandler

import "net/http"

type DecoratorFunc func(http.ResponseWriter, *http.Request, http.Handler)

// 3개의 인자를 갖는 펑션타입 = logger

type DecoHandler struct {
	fn DecoratorFunc // 펑션타입 변수 fn
	h  http.Handler  // http.Handler를 구현하고 있다 h
}

func (self *DecoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.fn(w, r, self.h)
	//자체로 http.Handler의 기능을 구현하고 있으며
	//호출될때 DecoratorFunc = logger 를 먼저 호출
}

func NewDecoHandler(h http.Handler, fn DecoratorFunc) http.Handler {
	return &DecoHandler{
		fn: fn,
		h:  h,
	}
}

ex) main

package main

import (
	"log"
	"net/http"
	"time"
	"web4/decoHandler"
	"web4/myapp"
)

// 로그를 알려주는  데코레이터
func logger(w http.ResponseWriter, r *http.Request, h http.Handler) {
	// h http.Hanler 기본기능을 받음
	start := time.Now()
	log.Print("[LOGGER1] Started")
	// Handler를 호출하기 전에 로그를 찍음
	h.ServeHTTP(w, r)
	// (기본기능) 핸들러 호출
	log.Println("[LOGGER1] Completed", time.Since(start).Milliseconds())
	// 완료된 시간을 알려준다.
}

func NewHandler() http.Handler {
	h := myapp.NewHandler()
	h = decoHandler.NewDecoHandler(h, logger)
	// myapp.에서 만들어온 핸들러를 deco로 감쌈
	// 인덱스 핸들러가 불릴때 log를 찍어준다.
	return h
}

func main() {
	mux := NewHandler()

	http.ListenAndServe(":3000", mux)
}
profile
개린이

0개의 댓글