Go言語(Golang)における簡単なWEBサーバーの構築



```go
package main

import (
	"errors"
	"fmt"
	"net/http"
)

func LogOutput(message string) {
	fmt.Println(message)

}

type SimpleDataStore struct {
	userData map[string]string
}

func (sds SimpleDataStore) UserNameForID(userID string) (string, bool) {
	name, ok := sds.userData[userID]
	return name, ok
}

func NewSimpleDataStore() SimpleDataStore {
	return SimpleDataStore{
		userData: map[string]string{
			"1": "Fred",
			"2": "Mary",
			"3": "Pat",
		},
	}
}

type DataStore interface {
	UserNameForID(userID string) (string, bool)
}

type Logger interface {
	Log(message string)
}

type LoggerAdapter func(message string)

func (lg LoggerAdapter) Log(message string) {
	lg(message)
}

type SimpleLogic struct {
	l  Logger
	ds DataStore
}

func (sl SimpleLogic) SayHello(userID string) (string, error) {
	sl.l.Log("SayHello(" + userID + ")")
	name, ok := sl.ds.UserNameForID(userID)
	if !ok {
		return "", errors.New("不明なユーザー")
	}
	return name + "さん、こんにちは。", nil
}

func (sl SimpleLogic) SayGoodbye(userID string) (string, error) {
	sl.l.Log("SayGoodbye(" + userID + ")")
	name, ok := sl.ds.UserNameForID(userID)
	if !ok {
		return "", errors.New("不明なユーザー")
	}
	return name + "さんさようなら。", nil
}

func NewSimpleLogic(l Logger, ds DataStore) SimpleLogic {
	return SimpleLogic{
		l:  l,
		ds: ds,
	}
}

type Logic interface {
	SayHello(userID string) (string, error)
}

type Controller struct {
	l     Logger
	logic Logic
}

func (c Controller) SayHello(w http.ResponseWriter, r *http.Request) {
	c.l.Log("SayHello内: ")
	userID := r.URL.Query().Get("user_id")
	message, err := c.logic.SayHello(userID)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(err.Error()))
		return
	}
	w.Write([]byte(message))
}

func NewController(l Logger, logic Logic) Controller {
	return Controller{
		l:     l,
		logic: logic,
	}
}

func main() {
	l := LoggerAdapter(LogOutput)
	ds := NewSimpleDataStore()
	logic := NewSimpleLogic(l, ds)
	c := NewController(l, logic)
	http.HandleFunc("/hello", c.SayHello)
	http.ListenAndServe(":8080", nil)
}

```

```go
func LogOutput(message string) {
	fmt.Println(message)
}
```
- `LogOutput`関数を定義します。この関数は、引数として受け取ったメッセージをコンソールに出力します。

```go
type SimpleDataStore struct {
    userData map[string]string
}
```
- `SimpleDataStore`という構造体を定義します。この構造体は、ユーザーIDと名前のペアを保持するための`userData`というマップを持っています。

```go
func (sds SimpleDataStore) UserNameForID(userID string) (string, bool) {
    name, ok := sds.userData[userID]
    return name, ok
}
```
- `UserNameForID`メソッドを定義します。このメソッドは、ユーザーIDを引数に取り、そのIDに対応する名前を検索します。見つかった場合は名前と`true`を、見つからなかった場合は空文字列と`false`を返します。

```go
func NewSimpleDataStore() SimpleDataStore {
    return SimpleDataStore{
        userData: map[string]string{
            "1": "Fred",
            "2": "Mary",
            "3": "Pat",
        },
    }
}
```
- `NewSimpleDataStore`関数を定義します。この関数は、初期化された`SimpleDataStore`を返します。ここでは、ユーザーIDと名前のマッピングが設定されています。

```go
type DataStore interface {
    UserNameForID(userID string) (string, bool)
}
```
- `DataStore`インターフェースを定義します。このインターフェースは、`UserNameForID`メソッドを要求します。これは、IDに基づいて名前を取得する機能を持つことを示しています。

```go
type Logger interface {
    Log(message string)
}
```
- `Logger`インターフェースを定義します。このインターフェースは、ログメッセージを出力するための`Log`メソッドを要求します。

```go
type LoggerAdapter func(message string)
```
- `LoggerAdapter`という関数型を定義します。この型は、`Log`メソッドを持つインターフェースに対応させるために使用されます。

```go
func (lg LoggerAdapter) Log(message string) {
    lg(message)
}
```
- `LoggerAdapter`に`Log`メソッドを実装します。このメソッドは、受け取ったメッセージを関数として呼び出します。これにより、適応された関数がロギング機能を持つことができます。

```go
type SimpleLogic struct {
    l  Logger
    ds DataStore
}
```
- `SimpleLogic`という構造体を定義します。この構造体は、ロギング機能(`l`)とデータストア(`ds`)を持ちます。これにより、ビジネスロジックを実装する際に両方を使用できます。

```go
func (sl SimpleLogic) SayHello(userID string) (string, error) {
    sl.l.Log("SayHello(" + userID + ")")
    name, ok := sl.ds.UserNameForID(userID)
    if !ok {
        return "", errors.New("不明なユーザー")
    }
    return name + "さん、こんにちは。", nil
}
```
- `SayHello`メソッドを定義します。このメソッドは、ユーザーIDを引数に取り、挨拶メッセージを生成します。
  - 最初に、`SayHello`が呼ばれたことをログに記録します。
  - 次に、データストアからユーザー名を取得します。
  - ユーザーが見つからない場合は、エラーを返します。
  - ユーザーが見つかれば、挨拶メッセージを返します。

```go
func (sl SimpleLogic) SayGoodbye(userID string) (string, error) {
    sl.l.Log("SayGoodbye(" + userID + ")")
    name, ok := sl.ds.UserNameForID(userID)
    if !ok {
        return "", errors.New("不明なユーザー")
    }
    return name + "さんさようなら。", nil
}
```
- `SayGoodbye`メソッドも`SayHello`と同様の構造ですが、さようならメッセージを生成します。

```go
func NewSimpleLogic(l Logger, ds DataStore) SimpleLogic {
    return SimpleLogic{
        l:    l,
        ds: ds,
    }
}
```
- `NewSimpleLogic`関数を定義します。この関数は、ロガー(`l`)とデータストア(`ds`)を受け取り、初期化された`SimpleLogic`を返します。

```go
type Logic interface {
    SayHello(userID string) (string, error)
}
```
- `Logic`インターフェースを定義します。このインターフェースは、`SayHello`メソッドを要求します。これは、ユーザーIDに基づいて挨拶を行うロジックを持つことを示しています。

```go
type Controller struct {
    l     Logger
    logic Logic
}
```
- `Controller`構造体を定義します。この構造体は、ロギング機能(`l`)とビジネスロジック(`logic`)を持ちます。HTTPリクエストを処理する役割を持っています。

```go
func (c Controller) SayHello(w http.ResponseWriter, r *http.Request) {
    c.l.Log("SayHello内: ")
    userID := r.URL.Query().Get("user_id")
    message, err := c.logic.SayHello(userID)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        w.Write([]byte(err.Error()))
        return
    }
    w.Write([]byte(message))
}
```
- `SayHello`メソッドを定義します。このメソッドは、HTTPリクエストを処理し、クエリパラメータから`user_id`を取得します。
  - 最初に、`SayHello`内でログを記録します。
  - 次に、ユーザーIDを用いてビジネスロジックの`SayHello`メソッドを呼び出します。
  - エラーが発生した場合は、HTTPステータスコード400（不正なリクエスト）を返し、エラーメッセージを表示します。
  - エラーがなければ、生成されたメッセージをHTTPレスポンスとして返します。

```go
func NewController(l Logger, logic Logic) Controller {
    return Controller{
        l:     l,
        logic: logic,
    }
}
```
- `NewController`関数を定義します。この関数は、ロガー(`l`)とロジック(`logic`)を受け取り、初期化された`Controller`を返します。

```go
func main() {
    l := LoggerAdapter(LogOutput)
    ds := NewSimpleDataStore()
    logic := NewSimpleLogic(l, ds)
    c := NewController(l, logic)
    http.HandleFunc("/hello", c.SayHello)
    http.ListenAndServe(":8080", nil)
}
```
- `main`関数は、プログラムのエントリーポイントです。
  - 最初に、`LogOutput`関数を`LoggerAdapter`としてラップします。
  - 次に、`NewSimpleDataStore`を呼び出してデータストアを初期化します。
  - その後、`NewSimpleLogic`を使ってビジネスロジックを初期化します。
  - `NewController`を使ってコントローラーを初期化します。
  - `http.HandleFunc`を使って、`/hello`エンドポイントへのリクエストを`SayHello`メソッドにマッピングします。
  - 最後に、`http.ListenAndServe`を使って、ポート8080でHTTPサーバーを起動します。



ブラウザで次のURLを表示してみてください（user_id= のあとに1, 2, 3などを指定）

http://localhost:8080/hello?user_id=1