함수형 옵션 패턴
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 79번째.
많은 선택적 구성 필드가 있는 구조체를 생성할 때 생성자 function은 다루기 어려워질 수 있습니다. Functional Options Pattern은 객체를 구성하는 function을 사용하여 이 문제를 우아하게 해결합니다.
여러 선택적 설정이 있는 서버 구조체를 생각해 보세요. 많은 매개변수를 사용하는 생성자 대신, 옵션 타입과 옵션을 반환하는 함수를 정의합니다.
type Server struct {
host string
port int
timeout int
}
type Option func(*Server)
func WithPort(port int) Option {
return func(s *Server) {
s.port = port
}
}
func WithTimeout(timeout int) Option {
return func(s *Server) {
s.timeout = timeout
}
}생성자는 가변 인수 옵션 목록을 받아 각각을 적용합니다:
func NewServer(host string, opts ...Option) *Server {
s := &Server{
host: host,
port: 8080, // 기본
timeout: 30, // 기본
}
for _, opt := range opts {
opt(s)
}
return s
}이제 서버를 생성하는 작업이 읽기 쉽고 유연해집니다:
// 기본값 사용
s1 := NewServer("localhost")
// 특정 옵션 사용자 지정
s2 := NewServer("localhost", WithPort(3000), WithTimeout(60))이 패턴이 뛰어난 이유는 호출자가 필요한 것만 지정하고, defaults가 한곳에서 명확하게 정의되며, 새로운 옵션을 추가해도 기존 코드가 깨지지 않기 때문입니다. 이 패턴은 grpc 및 zap과 같은 Go 라이브러리에서 널리 사용됩니다.
챌린지
쉬움Functional Options Pattern을 사용하여 구성 가능한 database connection을 만들어 봅시다! 이 패턴은 합리적인 기본값을 가진 선택적 설정이 많을 때 특히 유용하며, 이는 database connection에 정확히 필요한 방식입니다.
코드를 두 개의 파일로 구성합니다:
database.go: database connection type과 option functions를 Define합니다.다음의 unexported fields를 가진
DBConnectionstruct를 Create합니다:host(string)port(int)username(string)password(string)maxConnections(int)timeout(int) - 초 단위
*DBConnection을 modifies하는 function으로Optiontype을 Define합니다.Option을 반환하는 다음 option functions를 Create합니다:WithPort(port int)WithCredentials(username, password string)WithMaxConnections(max int)WithTimeout(seconds int)
*DBConnection을 반환하는 constructorNewDBConnection(host string, opts ...Option)을 Create합니다. 다음 defaults를 설정합니다: port5432, username"admin", password"secret", maxConnections10, timeout30. defaults를 설정한 후 제공된 모든 options를 Apply합니다.connection info를 다음 Format으로 반환하는
ConnectionString()method를 Add합니다:[username]:[password]@[host]:[port] (max:[maxConnections], timeout:[timeout]s)main.go: 다양한 구성으로 database connections를 만듭니다.host를 읽은 다음, Apply할 options의 수를 읽습니다. 각 option에 대해 option type과 해당 value(s)를 읽습니다:
- port 다음에 port number
- credentials 다음에 username과 password (두 줄)
- maxconn 다음에 max connections number
- timeout 다음에 초 단위의 timeout
지정된 options로
DBConnection을 Create하고 connection string을 출력합니다.
다음 inputs가 제공됩니다:
- Line 1: Host name
- Line 2: Number of options
- Following lines: Option type and value(s)
예를 들어, 다음과 같이 주어졌을 때:
localhost
0출력은 다음과 같아야 합니다:
admin:secret@localhost:5432 (max:10, timeout:30s)그리고 다음과 같이 주어졌을 때:
db.example.com
2
port
3306
credentials
root
mypassword출력은 다음과 같아야 합니다:
root:mypassword@db.example.com:3306 (max:10, timeout:30s)그리고 다음과 같이 주어졌을 때:
production.server
4
port
5433
credentials
dbuser
securepass123
maxconn
50
timeout
60출력은 다음과 같아야 합니다:
dbuser:securepass123@production.server:5433 (max:50, timeout:60s)호출자는 필요한 options만 지정하고, 지정하지 않은 settings에는 합리적인 defaults가 사용된다는 점에 주목하세요. 이것이 바로 Functional Options Pattern의 우아함입니다!
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 호스트 읽기
host, _ := reader.ReadString('\n')
host = strings.TrimSpace(host)
// 옵션 수 읽기
numOptionsStr, _ := reader.ReadString('\n')
numOptions, _ := strconv.Atoi(strings.TrimSpace(numOptionsStr))
// 옵션 수집
var opts []Option
for i := 0; i < numOptions; i++ {
optionType, _ := reader.ReadString('\n')
optionType = strings.TrimSpace(optionType)
switch optionType {
case "port":
portStr, _ := reader.ReadString('\n')
port, _ := strconv.Atoi(strings.TrimSpace(portStr))
// TODO: WithPort 옵션을 opts에 추가
_ = port // 구현 시 이 줄을 제거하세요
case "credentials":
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(username)
password, _ := reader.ReadString('\n')
password = strings.TrimSpace(password)
// TODO: WithCredentials 옵션을 opts에 추가
_, _ = username, password // 구현 시 이 줄을 제거하세요
case "maxconn":
maxStr, _ := reader.ReadString('\n')
maxConn, _ := strconv.Atoi(strings.TrimSpace(maxStr))
// TODO: WithMaxConnections 옵션을 opts에 추가
_ = maxConn // 구현 시 이 줄을 제거하세요
case "timeout":
timeoutStr, _ := reader.ReadString('\n')
timeout, _ := strconv.Atoi(strings.TrimSpace(timeoutStr))
// TODO: WithTimeout 옵션을 opts에 추가
_ = timeout // 구현 시 이 줄을 제거하세요
}
}
// TODO: host와 opts로 NewDBConnection을 사용하여 DBConnection 생성
// TODO: ConnectionString() 메서드를 사용하여 연결 문자열 출력
_ = opts // 구현 시 이 줄을 제거하세요
fmt.Println("TODO: Print connection string here")
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서직접 연습해 보세요: 온라인 Go 컴파일러