반응형
SMALL
합성(Composite)
합성 디자인 패턴은 상속(관계)보다 구성(일반적으로 a가 관계를 갖는 것으로 정의됨)을 선호합니다. 합성 디자인 패턴에서 객체의 계층 및 트리를 작성합니다. 개체 내부에 고유한 필드와 메서드가 있는 개체가 서로 다릅니다. 이 접근 방식은 매우 강력하며 상속 및 다중 상속의 많은 문제를 해결합니다.
package main
import "fmt"
type Athlete struct{}
func (a *Athlete) Train() {
fmt.Println("Training")
}
type Animal struct{}
func (a *Animal) Eat() {
fmt.Println("Eating")
}
type Shark struct {
Animal
Swim func()
}
type CompositeSwimmerA struct {
MyAthlete Athlete
MySwim func()
}
func Swim() {
fmt.Println("Swimming!")
}
type Swimmer interface {
Swim()
}
type Trainer interface {
Train()
}
type SwimmerImpl struct{}
func (s *SwimmerImpl) Swim() {
fmt.Println("Swimming!")
}
type CompositeSwimmerB struct {
Trainer
Swimmer
}
type Tree struct {
LeafValue int
Right *Tree
Left *Tree
}
func main() {
swimmerA := CompositeSwimmerA{
MySwim: Swim,
}
swimmerA.MyAthlete.Train()
swimmerA.MySwim()
fish := Shark{
Swim: Swim,
}
fish.Eat()
fish.Swim()
swimmerB := CompositeSwimmerB{
&Athlete{},
&SwimmerImpl{},
}
swimmerB.Trainer.Train()
swimmerB.Swimmer.Swim()
root := Tree{
LeafValue: 0,
Right: &Tree{
LeafValue: 5,
Right: &Tree{
LeafValue: 6,
Right: nil,
Left: nil,
},
Left: nil,
},
Left: &Tree{
LeafValue: 4,
Right: nil,
Left: nil,
},
}
fmt.Println(root.Right.Right.LeafValue)
}
어댑터(Adapter)
가장 일반적으로 사용되는 구조 패턴 중 하나는 어댑터 패턴입니다. 어댑터 패턴은 예를 들어 인터페이스가 오래되어 쉽게 또는 빠르게 교체할 수 없는 경우 매우 유용합니다. 대신 기존 인터페이스의 구현을 사용하는 응용프로그램의 현재 요구사항을 처리하기 위해 새 인터페이스를 만듭니다.
package adapter
import "fmt"
type LegacyPrinter interface {
Print(s string) string
}
type MyLegacyPrinter struct{}
func (l *MyLegacyPrinter) Print(s string) (newMsg string) {
newMsg = fmt.Sprintf("Legacy Printer: %s\n", s)
fmt.Println(newMsg)
return
}
type ModernPrinter interface {
PrintStored() string
}
type PrinterAdapter struct {
OldPrinter LegacyPrinter
Msg string
}
func (p *PrinterAdapter) PrintStored() (newMsg string) {
if p.OldPrinter != nil {
newMsg = fmt.Sprintf("Adapter: %s", p.Msg)
newMsg = p.OldPrinter.Print(newMsg)
} else {
newMsg = p.Msg
}
return
}
Test Code
package adapter
import "testing"
func TestAdapter(t *testing.T) {
msg := "Hello World!"
adapter := PrinterAdapter{
OldPrinter: &MyLegacyPrinter{},
Msg: msg,
}
returnedMsg := adapter.PrintStored()
if returnedMsg != "Legacy Printer: Adapter: Hello World!\n" {
t.Errorf("Message didn't match: %s\n", returnedMsg)
}
}
브릿지(Bridge)
브릿지 디자인 패턴은 객체와 객체의 기능을 분리하는 패턴입니다.
package bridge
import (
"errors"
"fmt"
"io"
)
type PrinterAPI interface {
PrintMessage(string) error
}
type PrinterImpl1 struct{}
func (p *PrinterImpl1) PrintMessage(msg string) error {
fmt.Printf("%s\n", msg)
return nil
}
type PrinterImpl2 struct {
Writer io.Writer
}
func (p *PrinterImpl2) PrintMessage(msg string) error {
if p.Writer == nil {
return errors.New("you need to pass an io.Writer to PrinterImpl2")
}
fmt.Fprintf(p.Writer, "%s", msg)
return nil
}
type TestWriter struct {
Msg string
}
func (t *TestWriter) Write(p []byte) (n int, err error) {
n = len(p)
if n > 0 {
t.Msg = string(p)
return n, nil
}
err = errors.New("content received on Writer was empty")
return
}
type PrinterAbstraction interface {
Print() error
}
type NormalPrinter struct {
Msg string
Printer PrinterAPI
}
func (c *NormalPrinter) Print() error {
c.Printer.PrintMessage(c.Msg)
return nil
}
type PacktPrinter struct {
Msg string
Printer PrinterAPI
}
func (c *PacktPrinter) Print() error {
c.Printer.PrintMessage(fmt.Sprintf("Message from Packt: %s", c.Msg))
return nil
}
Test Code
package bridge
import (
"strings"
"testing"
)
func TestPrintAPI1(t *testing.T) {
api1 := PrinterImpl1{}
err := api1.PrintMessage("Hello")
if err != nil {
t.Errorf("Error trying to use the API1 implementation: Message: %s\n",
err.Error())
}
}
func TestPrintAPI2(t *testing.T) {
api2 := PrinterImpl2{}
err := api2.PrintMessage("Hello")
if err != nil {
expectedErrorMessage := "you need to pass an io.Writer to PrinterImpl2"
if !strings.Contains(err.Error(), expectedErrorMessage) {
t.Errorf("Error message was not correct.\nActual: %s\nExpected: %s\n", err.Error(), expectedErrorMessage)
}
}
testWriter := TestWriter{}
api2 = PrinterImpl2{
Writer: &testWriter,
}
expectedMessage := "Hello"
err = api2.PrintMessage(expectedMessage)
if err != nil {
t.Errorf("Error trying to use the API2 implementation: %s\n", err.Error())
}
if testWriter.Msg != expectedMessage {
t.Fatalf("API2 did not write correctly on the io.Writer. \n Actual:%s\nExpected: %s\n", testWriter.Msg, expectedMessage)
}
}
func TestNormalPrinter_Print(t *testing.T) {
expectedMessage := "Hello io.Writer"
normal := NormalPrinter{
Msg: expectedMessage,
Printer: &PrinterImpl1{},
}
err := normal.Print()
if err != nil {
t.Errorf(err.Error())
}
testWriter := TestWriter{}
normal = NormalPrinter{
Msg: expectedMessage,
Printer: &PrinterImpl2{
Writer: &testWriter,
}}
err = normal.Print()
if err != nil {
t.Error(err.Error())
}
if testWriter.Msg != expectedMessage {
t.Errorf("The expected message on the io.Writer doesn't match actual.\nActual: %s\nExpected: %s\n", testWriter.Msg, expectedMessage)
}
}
func TestPacktPrinter_Print(t *testing.T) {
passedMessage := "Hello io.Writer"
expectedMessage := "Message from Packt: Hello io.Writer"
packt := PacktPrinter{
Msg: passedMessage,
Printer: &PrinterImpl1{},
}
err := packt.Print()
if err != nil {
t.Errorf(err.Error())
}
testWriter := TestWriter{}
packt = PacktPrinter{
Msg: passedMessage,
Printer: &PrinterImpl2{
Writer: &testWriter,
}}
err = packt.Print()
if err != nil {
t.Error(err.Error())
}
if testWriter.Msg != expectedMessage {
t.Errorf("The expected message on the io.Writer doesn't match actual.\nActual: %s\nExpected: %s\n", testWriter.Msg, expectedMessage)
}
}
반응형
LIST
'Go' 카테고리의 다른 글
[Design Pattern] 플라이웨이트(Flyweight), 전략(Strategy), 역할 사슬(Chain of Responsibility) 패턴 (0) | 2022.11.25 |
---|---|
[Design Pattern] 프록시(Proxy), 데코레이터(Decorator), 퍼사드(Facade) (0) | 2022.11.24 |
[Design Pattern] 추상 팩토리(Abstract Factory), 프로토타입(Prototype) 패턴 (0) | 2022.11.23 |
[Design Pattern] 싱글톤(Singleton), 빌더(Builder), 팩토리 메소드(Factory Method) 패턴 (0) | 2022.11.21 |
Go의 고루틴과 채널 (0) | 2022.01.10 |