Go

[Design Pattern] 추상 팩토리(Abstract Factory), 프로토타입(Prototype) 패턴

구루싸 2022. 11. 23. 11:49
반응형
SMALL

추상 팩토리(Abstract Factory)

추상 팩토리 디자인 패턴은 인터페이스를 통해 사용되는 더 큰(그리고 더 복잡한) 복합 객체를 달성하기 위한 새로운 그룹화 계층입니다. 패밀리에 객체를 그룹화하고 패밀리를 그룹화하는 아이디어는 서로 교환할 수 있고 더 쉽게 성장할 수 있는 대규모 공장을 갖는 것입니다. 개발 초기 단계에서는 코드를 시작하기 위해 모든 구체적인 구현이 완료될 때까지 기다리는 것보다 공장 및 추상 공장과 함께 작업하는 것이 더 쉽습니다. 또한 특정 필드에 대한 개체의 인벤토리가 매우 크고 쉽게 패밀리로 그룹화될 수 있다는 것을 알지 못하는 한 처음부터 추상 팩토리를 작성하지 않습니다. 개체 수가 너무 증가하여 개체 수를 모두 얻기 위해 고유한 점을 만드는 것이 유일한 방법일 때 관련 개체 패밀리를 그룹화하는 것이 매우 편리합니다. 

package abstractfactory

type Vehicle interface {
	NumWheels() int
	NumSeats() int
}
package abstractfactory

import "fmt"

const (
	CarFactoryType       = 1
	MotorbikeFactoryType = 2
)

type VehicleFactory interface {
	NewVehicle(v int) (Vehicle, error)
}

func BuildFactory(f int) (VehicleFactory, error) {
	switch f {
	case CarFactoryType:
		return new(CarFactory), nil
	case MotorbikeFactoryType:
		return new(MotorbikeFactory), nil
	default:
		return nil, fmt.Errorf("factory with id %d not recognized", f)
	}
}
package abstractfactory

type Motorbike interface {
	GetMotorbikeType() int
}

type SportMotorbike struct{}

func (s *SportMotorbike) NumWheels() int {
	return 2
}
func (s *SportMotorbike) NumSeats() int {
	return 1
}
func (s *SportMotorbike) GetMotorbikeType() int {
	return SportMotorbikeType
}

type CruiseMotorbike struct{}

func (c *CruiseMotorbike) NumWheels() int {
	return 2
}
func (c *CruiseMotorbike) NumSeats() int {
	return 2
}
func (c *CruiseMotorbike) GetMotorbikeType() int {
	return CruiseMotorbikeType
}
package abstractfactory

import "fmt"

const (
	SportMotorbikeType  = 1
	CruiseMotorbikeType = 2
)

type MotorbikeFactory struct{}

func (m *MotorbikeFactory) NewVehicle(v int) (Vehicle, error) {
	switch v {
	case SportMotorbikeType:
		return new(SportMotorbike), nil
	case CruiseMotorbikeType:
		return new(CruiseMotorbike), nil
	default:
		return nil, fmt.Errorf("vehicle of type %d not recognized", v)
	}
}
package abstractfactory

type Car interface {
	NumDoors() int
}

type LuxuryCar struct{}

func (*LuxuryCar) NumDoors() int {
	return 4
}
func (*LuxuryCar) NumWheels() int {
	return 4
}
func (*LuxuryCar) NumSeats() int {
	return 5
}

type FamilyCar struct{}

func (*FamilyCar) NumDoors() int {
	return 5
}

func (*FamilyCar) NumWheels() int {
	return 4
}

func (*FamilyCar) NumSeats() int {
	return 5
}
package abstractfactory

import "fmt"

const (
	LuxuryCarType = 1
	FamilyCarType = 2
)

type CarFactory struct{}

func (c *CarFactory) NewVehicle(v int) (Vehicle, error) {
	switch v {
	case LuxuryCarType:
		return new(LuxuryCar), nil
	case FamilyCarType:
		return new(FamilyCar), nil
	default:
		return nil, fmt.Errorf("vehicle of type %d not recognized", v)
	}
}

Test Code

 

package abstractfactory

import "testing"

func TestMotorbikeFactory(t *testing.T) {
	motorbikeFactory, err := BuildFactory(MotorbikeFactoryType)
	if err != nil {
		t.Fatal(err)
	}

	sportMotorbikeVehicle, err := motorbikeFactory.NewVehicle(SportMotorbikeType)
	if err != nil {
		t.Fatal(err)
	}

	t.Logf("motorbike vehicle has %d wheels", sportMotorbikeVehicle.NumWheels())
	t.Logf("motorbike vehicle has %d seats", sportMotorbikeVehicle.NumSeats())

	sportMotorbike, ok := sportMotorbikeVehicle.(Motorbike)
	if !ok {
		t.Fatal("struct assertion has failed")
	}

	t.Logf("sport motorbike has type %d", sportMotorbike.GetMotorbikeType())

	cruiseMotorbikeVehicle, err := motorbikeFactory.NewVehicle(CruiseMotorbikeType)
	if err != nil {
		t.Fatal(err)
	}

	t.Logf("motorbike vehicle has %d wheels", cruiseMotorbikeVehicle.NumWheels())
	t.Logf("motorbike vehicle has %d seats", sportMotorbikeVehicle.NumSeats())

	cruiseMotorbike, ok := cruiseMotorbikeVehicle.(Motorbike)
	if !ok {
		t.Fatal("struct assertion has failed")
	}

	t.Logf("sport motorbike has type %d", cruiseMotorbike.GetMotorbikeType())
}

func TestCarFactory(t *testing.T) {
	carFactory, err := BuildFactory(CarFactoryType)
	if err != nil {
		t.Fatal(err)
	}

	luxuryCarVehicle, err := carFactory.NewVehicle(LuxuryCarType)
	if err != nil {
		t.Fatal(err)
	}

	t.Logf("car vehicle has %d wheels\n", luxuryCarVehicle.NumWheels())
	t.Logf("car vehicle has %d seats\n", luxuryCarVehicle.NumSeats())

	luxuryCar, ok := luxuryCarVehicle.(Car)
	if !ok {
		t.Fatal("struct assertion has failed")
	}
	t.Logf("luxury car has %d doors", luxuryCar.NumDoors())

	familyCarVehicle, err := carFactory.NewVehicle(FamilyCarType)
	if err != nil {
		t.Fatal(err)
	}

	t.Logf("car vehicle has %d wheels", familyCarVehicle.NumWheels())
	t.Logf("car vehicle has %d seats", luxuryCarVehicle.NumSeats())

	familyCar, ok := familyCarVehicle.(Car)
	if !ok {
		t.Fatal("struct assertion has failed")
	}
	t.Logf("family car has %d doors", familyCar.NumDoors())

}

func TestFactoryNonExistent(t *testing.T) {
	_, err := BuildFactory(3)
	if err == nil {
		t.Error("a factory with ID 3 must return an error")
	}
	t.Log("LOG:", err)
}

프로토타입(Prototype)

프로토타입의 목적은 컴파일 시 이미 생성되었지만 런타임에 원하는 횟수만큼 복제할 수 있는 개체 또는 개체 집합을 갖는 것입니다. 이 기능은 웹 페이지에 등록한 지 얼마 되지 않은 사용자의 기본 템플릿이나 일부 서비스의 기본 요금제로 유용합니다. 이 패턴과 Builder 패턴의 주요 차이점은 객체가 런타임에 빌드되지 않고 사용자를 위해 복제된다는 것입니다. 프로토타입을 사용하여 정보를 저장하는 캐시와 같은 솔루션을 구축할 수도 있습니다.

package prototype

import (
	"errors"
	"fmt"
)

const (
	White = 1
	Black = 2
	Blue  = 3
)

var whitePrototype *Shirt = &Shirt{
	Price: 15.00,
	SKU:   "empty",
	Color: White,
}

var blackPrototype *Shirt = &Shirt{
	Price: 16.00,
	SKU:   "empty",
	Color: Black,
}
var bluePrototype *Shirt = &Shirt{
	Price: 17.00,
	SKU:   "empty",
	Color: Blue,
}

type ShirtCloner interface {
	GetClone(s int) (ItemInfoGetter, error)
}

func GetShirtCloner() ShirtCloner {
	return new(ShirtCache)
}

type ShirtCache struct{}

func (s *ShirtCache) GetClone(m int) (ItemInfoGetter, error) {
	switch m {
	case White:
		newItem := *whitePrototype
		return &newItem, nil
	case Black:
		newItem := *blackPrototype
		return &newItem, nil
	case Blue:
		newItem := *bluePrototype
		return &newItem, nil
	default:
		return nil, errors.New("Shirt model not recognized")
	}
}

type ItemInfoGetter interface {
	GetInfo() string
}

type ShirtColor byte

type Shirt struct {
	Price float32
	SKU   string
	Color ShirtColor
}

func (s *Shirt) GetInfo() string {
	return fmt.Sprintf("Shirt with SKU '%s' and Color id %d that costs %f\n", s.SKU, s.Color, s.Price)
}

func (s *Shirt) GetPrice() float32 {
	return s.Price
}

프로토타입 패턴은 캐시 및 기본 개체를 구축하는 강력한 도구입니다.

반응형
LIST