go log("hello")
계층구조가 없는 유연한 타입 시스템을 제공하기 때문에 리팩토링에 대한 부담을 최소화 하면서 코드 재사용가능하다. 기능을 재사용하기 위해 타입을 임베드하는 형식의 합성(composition)을 이용한다. 다른 언어들도 상속을 사용하여 composition 하지만, 의존성의 너무 강하게 연결되어 결국에는 코드 재사용이 복잡해졌지만, Go는 가볍다.
interface User{
public void login();
public void logout();
}
다음과 같이 Java에서 이 인터페이스를 구현하려면 User인터페이스에 정의된 모든 약속들을 만족하는 클래스 구현 후, 그 클래스가 User 구현하고 있음을 명시적으로 선언해야 한다. 반면 Go의 인터페이스는 주로 하나의 동작만을 표현한다. 예를 들면 다음과 같다.
type User struct { // User 구조체 선언
}
type NotUser struct{ma
}
func (u User) login(){
println("user login")
}
func (n NotUser) login(){
println("Notuser login")
}
즉 이전 객체지향언어에서는 객체를 중심으로 구현하게 됐지만, Go는 기능을 중심으로 인터페이스를 형성 할 수 있다. 이는 코드의 재사용과 합성에서 큰 이점을 얻을 수 있다.
코드주소: https://github.com/webgenie/go-in-action
다음과 같은 아키텍쳐를 가진 프로젝트를 실습해보자.
package main
import (
"chaper2/sample/search"
"log"
"os"
)
// init 함수는 main 함수보다 먼저 호출된다.
func init() {
// 표준 출력으로 로그를 출력하도록 변경한다.
log.SetOutput(os.Stdout)
}
// main 함수는 프로그램의 진입점이다.
func main() {
// 지정된 검색어로 검색을 수행한다.
search.Run("Sherlock Holmes")
}
package search
import (
"log"
"sync"
)
// 검색을 처리할 검색기의 매핑 정보를 저장할 맵(map)
var matchers = make(map[string]Matcher)
// 검색 로직을 수행할 Run 함수
func Run(searchTerm string) {
// 검색할 피드의 목록을 조회한다.
feeds, err := RetrieveFeeds()
if err != nil {
log.Fatal(err)
}
// 언버퍼드 채널을 생성하여 화면에 표시할 검색 결과를 전달 받는다.
results := make(chan *Result)
// 모든 피드를 처리할 때까지 기다릴 대기 그룹(Wait group)을 설정한다.
var waitGroup sync.WaitGroup
// 개별 피드를 처리하는 동안 대기해야 할
// 고루틴의 갯수를 설정한다.
waitGroup.Add(len(feeds))
// 각기 다른 종류의 피드를 처리할 고루틴을 실행한다.
for _, feed := range feeds {
// 검색을 위해 검색기를 조회한다.
matcher, exists := matchers[feed.Type]
if !exists {
matcher = matchers["default"]
}
// 검색을 실행하기 위해 고루틴을 실행힌다.
go func(matcher Matcher, feed *Feed) {
Match(matcher, feed, searchTerm, results)
waitGroup.Done()
}(matcher, feed)
}
// 모든 작업이 완료되었는지를 모니터링할 고루틴을 실행한다.
go func() {
// 모든 작업이 처리될 때까지 기다린다.
waitGroup.Wait()
// Display 함수에게 프로그램을 종료할 수 있음을
// 알리기 위해 채널을 닫는다.
close(results)
}()
// 검색 결과를 화면에 표시하고
// 마지막 결과를 표시한 뒤 리턴한다.
Display(results)
}
// 프로그램에서 사용할 검색기를 등록할 함수를 정의한다.
func Register(feedType string, matcher Matcher) {
if _, exists := matchers[feedType]; exists {
log.Fatalln(feedType, "검색기가 이미 등록되었습니다.")
}
log.Println("등록 완료:", feedType, " 검색기")
matchers[feedType] = matcher
}