반응형
SMALL
플라이웨이트(Flyweight)
플라이웨이트 디자인 패턴은 특정 유형의 여러 인스턴스 간에 무거운 개체의 상태를 공유할 수 있는 패턴입니다.
package flyweight
import "time"
const (
TEAM_A = iota
TEAM_B
)
type Team struct {
ID uint64
Name int
Shield []byte
Players []Player
HistoricalData []HistoricalData
}
type Player struct {
Name string
Surname string
PreviousTeam uint64
Photo []byte
}
type HistoricalData struct {
Year uint8
LeagueResults []Match
}
type Match struct {
Date time.Time
VisitorID uint64
LocalID uint64
LocalScore byte
VisitorScore byte
LocalShoots uint16
VisitorShoots uint16
}
type teamFlyweightFactory struct {
createdTeams map[int]*Team
}
func NewTeamFactory() teamFlyweightFactory {
return teamFlyweightFactory{
createdTeams: make(map[int]*Team),
}
}
func (t *teamFlyweightFactory) GetTeam(teamID int) *Team {
if t.createdTeams[teamID] != nil {
return t.createdTeams[teamID]
}
team := getTeamFactory(teamID)
t.createdTeams[teamID] = &team
return t.createdTeams[teamID]
}
func (t *teamFlyweightFactory) GetNumberOfObjects() int {
return len(t.createdTeams)
}
func getTeamFactory(team int) Team {
switch team {
case TEAM_B:
return Team{
ID: 2,
Name: TEAM_B,
}
default:
return Team{
ID: 1,
Name: TEAM_A,
}
}
}
Test Code
package flyweight
import (
"fmt"
"testing"
)
func TestTeamFlyweightFactory_GetTeam(t *testing.T) {
factory := NewTeamFactory()
teamA1 := factory.GetTeam(TEAM_A)
if teamA1 == nil {
t.Error("The pointer to the TEAM_A was nil")
}
teamA2 := factory.GetTeam(TEAM_A)
if teamA2 == nil {
t.Error("The pointer to the TEAM_A was nil")
}
if teamA1 != teamA2 {
t.Error("TEAM_A pointers weren't the same")
}
if factory.GetNumberOfObjects() != 1 {
t.Errorf("The number of objects created was not 1: %d\n", factory.GetNumberOfObjects())
}
}
func Test_HighVolume(t *testing.T) {
factory := NewTeamFactory()
teams := make([]*Team, 500000*2)
for i := 0; i < 500000; i++ {
teams[i] = factory.GetTeam(TEAM_A)
}
for i := 500000; i < 2*500000; i++ {
teams[i] = factory.GetTeam(TEAM_B)
}
if factory.GetNumberOfObjects() != 2 {
t.Errorf("The number of objects created was not 2: %d\n", factory.GetNumberOfObjects())
}
for i := 0; i < 3; i++ {
fmt.Printf("Pointer %d points to %p and is located in %p\n", i, teams[i], &teams[i])
}
}
전략(Strategy)
전략 디자인 패턴은 특정 기능을 달성하기 위해 서로 다른 알고리즘을 사용합니다. 이러한 알고리즘은 인터페이스 뒤에 숨겨져 있으며, 물론 교환 가능해야 합니다. 모든 알고리즘이 다른 방식으로 동일한 기능을 달성합니다.
package main
import (
"flag"
"log"
"os"
"github.com/leo-themedium/golang/designPatterns/strategy/shapes"
)
var output = flag.String("output", "console", "The output to use between 'console' and 'image' file")
func main() {
flag.Parse()
activeStrategy, err := shapes.Factory(*output)
if err != nil {
log.Fatal(err)
}
switch *output {
case shapes.TEXT_STRATEGY:
activeStrategy.SetWriter(os.Stdout)
case shapes.IMAGE_STRATEGY:
w, err := os.Create("/tmp/image.jpg")
if err != nil {
log.Fatal("Error opening image")
}
defer w.Close()
activeStrategy.SetWriter(w)
}
err = activeStrategy.Draw()
if err != nil {
log.Fatal(err)
}
}
package strategy
import "io"
type Output interface {
Draw() error
SetLog(io.Writer)
SetWriter(io.Writer)
}
type DrawOutput struct {
Writer io.Writer
LogWriter io.Writer
}
func (d *DrawOutput) SetLog(w io.Writer) {
d.LogWriter = w
}
func (d *DrawOutput) SetWriter(w io.Writer) {
d.Writer = w
}
package shapes
import (
"fmt"
"os"
"github.com/leo-themedium/golang/designPatterns/strategy"
)
const (
TEXT_STRATEGY = "text"
IMAGE_STRATEGY = "image"
)
func Factory(s string) (strategy.Output, error) {
switch s {
case TEXT_STRATEGY:
return &TextSquare{
DrawOutput: strategy.DrawOutput{
LogWriter: os.Stdout,
},
}, nil
case IMAGE_STRATEGY:
return &ImageSquare{
DrawOutput: strategy.DrawOutput{
LogWriter: os.Stdout,
},
}, nil
default:
return nil, fmt.Errorf("Strategy '%s' not found\n", s)
}
}
package shapes
import (
"fmt"
"image"
"image/color"
"image/draw"
"image/jpeg"
"github.com/leo-themedium/golang/designPatterns/strategy"
)
type ImageSquare struct {
strategy.DrawOutput
}
func (i *ImageSquare) Draw() error {
width := 800
height := 600
bgColor := image.Uniform{color.RGBA{0, 0, 0, 1}}
origin := image.Point{0, 0}
quality := &jpeg.Options{Quality: 75}
bgRectangle := image.NewRGBA(image.Rectangle{
Min: origin,
Max: image.Point{X: width, Y: height},
})
draw.Draw(bgRectangle, bgRectangle.Bounds(), &bgColor, origin, draw.Src)
squareWidth := 200
squareHeight := 200
squareColor := image.Uniform{color.RGBA{R: 255, G: 0, B: 0, A: 1}}
square := image.Rect(0, 0, squareWidth, squareHeight)
square = square.Add(image.Point{
X: (width / 2) - (squareWidth / 2),
Y: (height / 2) - (squareHeight / 2),
})
squareImg := image.NewRGBA(square)
draw.Draw(bgRectangle, squareImg.Bounds(), &squareColor, origin, draw.Src)
if i.Writer == nil {
return fmt.Errorf("No writer stored on ImageSquare")
}
if err := jpeg.Encode(i.Writer, bgRectangle, quality); err != nil {
return fmt.Errorf("Error writing image to disk")
}
if i.LogWriter != nil {
i.LogWriter.Write([]byte("Image written in provided writer\n"))
}
return nil
}
package shapes
import "github.com/leo-themedium/golang/designPatterns/strategy"
type TextSquare struct {
strategy.DrawOutput
}
func (t *TextSquare) Draw() error {
t.Writer.Write([]byte("Circle"))
return nil
}
역할 사슬(Chain of Responsibility)
단일 책임 원칙은 유형, 기능, 방법 또는 이와 유사한 추상화가 하나의 단일 책임만 가지고 있어야 함을 의미하고, 이를 상당히 잘 수행해야 한다는 것을 의미합니다. 이러한 방식으로, 우리는 특정한 한 가지를 각각 달성하는 많은 기능을 일부 구조, 슬라이스, 맵 등에 적용할 수 있습니다. 이러한 추상화 중 많은 부분을 논리적인 방식으로 순서대로 실행되도록 체인화할 수 있습니다.
package chainofresponsibility
import (
"fmt"
"io"
"strings"
)
type ChainLogger interface {
Next(string)
}
type FirstLogger struct {
NextChain ChainLogger
}
func (fl *FirstLogger) Next(s string) {
fmt.Printf("First logger: %s\n", s)
if fl.NextChain != nil {
fl.NextChain.Next(s)
}
}
type SecondLogger struct {
NextChain ChainLogger
}
func (sl *SecondLogger) Next(s string) {
if strings.Contains(strings.ToLower(s), "hello") {
fmt.Printf("Second logger: %s\n", s)
if sl.NextChain != nil {
sl.NextChain.Next(s)
}
return
}
fmt.Printf("Finishing in second logging\n\n")
}
type WriterLogger struct {
NextChain ChainLogger
Writer io.Writer
}
func (wl *WriterLogger) Next(s string) {
if wl.Writer != nil {
wl.Writer.Write([]byte("WriterLogger: " + s))
}
if wl.NextChain != nil {
wl.NextChain.Next(s)
}
}
Test Code
package chainofresponsibility
import (
"fmt"
"strings"
"testing"
)
type myTestWriter struct {
receivedMessage *string
}
func (m *myTestWriter) Write(p []byte) (int, error) {
if m.receivedMessage == nil {
m.receivedMessage = new(string)
}
tempMessage := fmt.Sprintf("%s%s", *m.receivedMessage, p)
m.receivedMessage = &tempMessage
return len(p), nil
}
func (m *myTestWriter) Next(s string) {
m.Write([]byte(s))
}
func TestCreateDefaultChain(t *testing.T) {
//Our test ChainLogger
myWriter := myTestWriter{}
writerLogger := WriterLogger{Writer: &myWriter}
second := SecondLogger{NextChain: &writerLogger}
chain := FirstLogger{NextChain: &second}
t.Run("3 loggers, 2 of them writes to console, second only if it founds "+
"the word 'hello', third writes to some variable if second found 'hello'",
func(t *testing.T) {
chain.Next("message that breaks the chain\n")
if myWriter.receivedMessage != nil {
t.Fatal("Last link should not receive any message")
}
chain.Next("Hello\n")
if !strings.Contains(*myWriter.receivedMessage, "Hello") {
t.Fatal("Last link didn't received expected message")
}
})
}
반응형
LIST
'Go' 카테고리의 다른 글
[Design Pattern] 인터프리터(Interpreter), 방문자(Visitor), 상태(State) 패턴 (0) | 2022.11.30 |
---|---|
[Design Pattern] 커맨드(Command), 템플릿(Template), 메멘토(Memento) 패턴 (0) | 2022.11.29 |
[Design Pattern] 프록시(Proxy), 데코레이터(Decorator), 퍼사드(Facade) (0) | 2022.11.24 |
[Design Pattern] 합성(Composite), 어댑터(Adapter), 브릿지(Bridge) 패턴 (0) | 2022.11.23 |
[Design Pattern] 추상 팩토리(Abstract Factory), 프로토타입(Prototype) 패턴 (0) | 2022.11.23 |