Go Lang/Study

[GoLang] 추상 팩토리 디자인 사용해보기

DSeung 2025. 1. 17. 19:41

추상 팩토리 디자인 패턴

추상 팩토리 디자인을 사용해 보겠습니다.

아마 여러분이 디자인 패턴을 한 번이라도 봤다면 이 패턴을 가장 처음 보게 될 텐데

이게 디자인 패턴 중 가장 친근할 겁니다.

 

요약하자면 

일정한 패턴을 가진 객체들을 자주 사용할 때

공통된 함수나 변수로 묶어서 인터페이스를 만들고, 이 인터페이스를 잘 활용을 하는 방법입니다.

 

이걸 구현하는 하위 객체들을 사용하는, 호출 부에서는 객체 하나하나의 생성을 로직을 몰라도 바로 쓸 수 있습니다.

이로써 객체 생성의 추상화가 구현됨으로써 유연성확장성을 높여 줍니다.

 

개념

추상화라는 말이 들어가는 걸로, 알 수 있듯이 객체 지향 언어에서 인터페이스를 사용해서 주로 구현합니다.

  • 여러 객체를 생성하기 위해 이들을 묶는 상위 개념의 인터페이스 제공
  • 각 객체는 Factory (생성 함수)를 통해 생성됨
  • 클라이언트는 Factory(생성 함수)의 인터페이스만 알면 됨

 

이로 인해 아래 장점을 얻습니다.

  • 객체 생성을 팩토리 함수에서 함으로써 클라이언트는 생성 방법을 몰라도 됨
    이게 가장 큰 장점으로 일일이 로직을 분석하지 않아도 돼서 유지보수에서 용이함
  • 동일한 팩토리 함수를 쓰므로 객체 간의 호환성을 유지
  • 이미 있는 패턴을 따라 하면 되므로 추후에 기능을 추가하기도 유리

즉 아래 이미지로 요약이 가능합니다

 

 

사용 예시로는 무엇을 할까 고민했는데..

 

제가 지금 다니는 회사는 안과 데이터를 다루는데.

 

이때 한 환자에 대해 여러 진단이 들어가게 됩니다.

그때 아주 용이하게 쓸 수 있을 것 같아서 이걸로 예시를 들었습니다.

 

진단마다 고정된 정보가 있을 수 있고, 반면에 진단마다 고유의 정보 또한 존재합니다.

즉 진단이라는 개념으로 여러 진단을 묶을 수 있죠.

 

예제

제가 예시로 든 상황을 코드로 한번 구현해 봅시다

저는 go 1.21.6을 사용했습니다.

 

우선 추상 팩토리를 만들어줍니다

diagnostic/define.go 

package diagnostic

import "fmt"

type Diagnosis interface {
	GetType() string
	GetDetails() string
}

// Diagnosis 를 구현하는 Topography
type Topography struct {
	Curvature string
	Surface   string
}

func (t Topography) GetType() string {
	return "Topography"
}

func (t Topography) GetDetails() string {
	return "Curvature: " + t.Curvature + ", Surface: " + t.Surface
}

// Diagnosis 를 구현하는 AxialLength
type AxialLength struct {
	Length float64
}

func (a AxialLength) GetType() string {
	return "AxialLength"
}

func (a AxialLength) GetDetails() string {
	return "Length: " + fmt.Sprintf("%.2f mm", a.Length)
}

// Diagnosis 를 구현하는 AxialLength
type VisualField struct {
	Count   int
	Success int
	Fail    int
}

func (v VisualField) GetType() string {
	return "VisualField"
}

func (v VisualField) GetDetails() string {
	return fmt.Sprintf("Count: %d", v.Count)
}

 

그 후 팩토리 함수를 만듭니다.

diagnostic/factory.go

package diagnostic

// DiagnosisFactory 추상 팩토리 인터페이스
type DiagnosisFactory interface {
	CreateDiagnosis() Diagnosis
}

// TopographyFactory 생성 팩토리 선언
type TopographyFactory struct {
	Curvature string
	Surface   string
}

func (f TopographyFactory) CreateDiagnosis() Diagnosis {
	return Topography{
		Curvature: f.Curvature,
		Surface:   f.Surface,
	}
}

// AxialLengthFactory 생성 팩토리 선언
type AxialLengthFactory struct {
	Length float64
}

func (f AxialLengthFactory) CreateDiagnosis() Diagnosis {
	return AxialLength{
		Length: f.Length,
	}
}

// VisualFieldFactory 생성 팩토리 선언
type VisualFieldFactory struct {
	Count   int
	Success int
	Fail    int
}

func (f VisualFieldFactory) CreateDiagnosis() Diagnosis {
	return VisualField{
		Count:   f.Count,
		Success: f.Success,
		Fail:    f.Fail,
	}
}

 

이제 main.go에서 아래처럼 사용이 가능합니다.

package main

import (
	"design-pattern.com/diagnostic"
	"fmt"
)

type Patient struct {
	Name      string
	Diagnoses []diagnostic.Diagnosis
}

func (p *Patient) AddDiagnosis(d diagnostic.Diagnosis) {
	p.Diagnoses = append(p.Diagnoses, d)
}

func (p Patient) PrintDiagnoses() {
	fmt.Println("Patient Name:", p.Name)
	for _, d := range p.Diagnoses {
		fmt.Printf("- [%s] %s\n", d.GetType(), d.GetDetails())
	}
}

func main() {
	// 환자 생성
	patient := Patient{Name: "Seung"}

	topography := diagnostic.TopographyFactory{
		Curvature: "Steep",
		Surface:   "Smooth",
	}.CreateDiagnosis()
	axialLength := diagnostic.AxialLengthFactory{
		Length: 23.45,
	}.CreateDiagnosis()
	visualField := diagnostic.VisualFieldFactory{
		Count:   12,
		Success: 8,
		Fail:    4,
	}.CreateDiagnosis()

	// 환자에 진단을 넣을 때와
	patient.AddDiagnosis(topography)
	patient.AddDiagnosis(axialLength)
	patient.AddDiagnosis(visualField)

	// 데이터를 출력할 때 기존 내용을 전혀 몰라도 됨 => 추상 팩토리 사용 이유
	patient.PrintDiagnoses()
}

 

이걸 사용해보면 아래 같은 결과를 봅니다

Patient Name: Seung
- [Topography] Curvature: Steep, Surface: Smooth
- [AxialLength] Length: 23.45 mm
- [VisualField] Count: 12

 

여태까지의 로직이나 패턴은 요약하면 아래 그림과 같죠 

실제 적용은..?

이 패턴을 이전 회사에서 Laravel을 다룰 때도 기존 프로젝트에 적용을 시켜본 적이 있었는데.

이 패턴을 설명해 줘도 같이 일하던 팀원이 필요성을 못 느껴서 적용하기 꺼려하던 경험이 있습니다.

 

끝내 프로젝트에 적용했지만 "왜 이걸 사용하지"라는 느낌을 지우지 못하더군요

이 부분은 지금은 더 잘 설득할 수 있을 텐데, 제가 부족했죠.

 

다시 돌아와서 이 패턴은 유지보수에서 엄청난 이점이 있는 건 맞지만

처음 작성하는 코드의 양이 배 이상 늘어나게 되니 작업 속도가 느려지는 경향이 있습니다.

 

지금 회사에 적용해 볼까 했지만 이미.. 많은 일이 쌓여있기 때문에

나중을 기약할 것 같습니다..

반응형