シングルトン設計パターンは、ソフトウェア プログラミングで最も重要で頻繁に使用されるパターンの 1 つです。これにより、アプリケーションの実行時にクラスにインスタンスが 1 つだけ存在することが保証され、そのインスタンスへのグローバル アクセス ポイントが提供されます。この記事では、シングルトンの重要性、Golang でのシングルトンの実装方法、特に同時環境においてシングルトンがもたらす利点について説明します。
シングルトンとは何ですか?
シングルトンは、クラスのインスタンスを単一のインスタンスに制限する設計パターンです。これは、次のような単一の制御点または単一の共有リソースが必要な状況で特に役立ちます。
構成マネージャー。アプリケーション設定を一元管理する必要があります。-
データベース接続プール。限られた数の接続を効率的に管理する必要があります。-
ロガー。ログの一貫性が重要です。-
シングルトンを使用する理由
パターンの実装に関する、より意味のあるいくつかのポイントをリストします。また、すべてがバラ色であるわけではなく、それによって発生する可能性のある問題のいくつかも示すつもりです。
利点
グローバルな一貫性: アプリケーションのすべてのポイントが同じインスタンスを使用することを保証し、データと動作の一貫性を提供します。-
アクセス制御: インスタンスの作成とアクセスを一元管理し、オブジェクトのライフサイクルのメンテナンスと管理を容易にします。-
リソース効率: 複数のインスタンスの不必要な作成を回避し、メモリと処理リソースを節約します。-
短所
テストの難易度: シングルトンでは管理が必要なグローバル状態が導入されるため、単体テストの作成がより困難になる可能性があります。-
結合の増加: シングルトンを過度に使用すると、コンポーネント間の結合が強化され、アプリケーションの維持と進化が困難になる可能性があります。-
シングルトンの実装
シングルトンを実装するには Golang を使用します。この言語では、複数のゴルーチンが同時にインスタンスにアクセスしようとする場合でも、インスタンスが 1 つだけ作成されるようにするために、同時実行性に特別な注意を払う必要があります。
この例を現実の世界に近づけるために、アプリケーション用のロガーを作成してみましょう。ロガーはアプリケーションの一般的なツールであり、ログの一貫性を確保するために一意である必要があります。
1 - 構造の定義
まず、単一のインスタンスを持つ構造を定義します。
パッケージロガー
輸入 (
「fmt」
「同期」
)
ロガー構造体型 {}
var loggerInstance *ロガー
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
2 - NewInstance 関数の実装
NewInstance 関数は、Singleton 構造の単一インスタンスを返す役割を果たします。同時環境でのセキュリティを確保するためにミューテックスを使用し、効率性を高めるために二重チェックのロックを実装します。
パッケージロガー
輸入 (
「fmt」
「同期」
)
ロガー構造体の型{}
var logger *ロガー
var mtx = &sync.Mutex{}
func NewInstance() *ロガー {
if ロガー == nil {
mtx.Lock()
defer mtx.Unlock()
if ロガー == nil {
fmt.Println("新しいロガーの作成")
ロガー = &ロガー{}
}
} それ以外 {
fmt.Println("ロガーはすでに作成されています")
}
ロガーを返す
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
3 - ログタイプの実装
ログ ツールには、情報のみを表示する Info、エラーを表示する Error など、常にいくつかのログ タイプがあります。これは、アプリケーションに表示したい情報の種類をフィルターする方法でもあります。
それでは、Info タイプのログを表示するメソッドを作成しましょう。これを行うには、ログ メッセージを受信し、それを INFO 形式にフォーマットする関数を作成します。
パッケージロガー
輸入 (
「fmt」
「同期」
"チーム"
)
定数(
情報文字列 = "情報"
)
ロガー構造体の型{}
var logger *ロガー
var mtx = &sync.Mutex{}
func NewInstance() *ロガー {
if ロガー == nil {
mtx.Lock()
defer mtx.Unlock()
if ロガー == nil {
fmt.Println("新しいロガーの作成")
ロガー = &ロガー{}
}
} それ以外 {
fmt.Println("ロガーはすでに作成されています")
}
ロガーを返す
}
func (l *Logger) Info(メッセージ文字列) {
fmt.Printf("%s - %s: %s\n", time.Now().UTC().Format(time.RFC3339Nano), INFO, message)
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
4 - ロガーの使用
そして、新しいロガーを使用するには、メイン パッケージ内でロガーをインスタンス化し、ログを作成して、この実装がどのように機能するかを確認します。
パッケージメイン
輸入 (
「playground-go/pkg/logger」
)
関数 main() {
ログ := logger.NewInstance()
log.Info("これはログの例です")
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
これはプログラムを実行したときの結果です:
新しいロガーの作成
2024-07-03T19:34:57.609599Z - 情報: これはログの例です
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
NewInstance がインスタンスを 1 つだけ実行することを本当に保証しているかどうかをテストしたい場合は、次のテストを実行できます。
パッケージメイン
輸入 (
「fmt」
「playground-go/pkg/logger」
)
関数 main() {
ログ := logger.NewInstance()
log.Info("これはログの例です")
log2 := logger.NewInstance()
log2.Info("これはログの別の例です")
if log == log2 {
fmt.Println("同じインスタンス")
} それ以外 {
fmt.Println("別のインスタンス")
}
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
ログが変更され、新しいインスタンスの作成がブロックされたことがわかります:
新しいロガーの作成
2024-07-03T19:45:19.603783Z - 情報: これはログの例です
ロガーはすでに作成されています
2024-07-03T19:45:19.603793Z - 情報: これはログの別の例です
同じインスタンス
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
結論
シングルトン パターンは、アプリケーションの実行時に特定のクラスのインスタンスが 1 つだけ存在することを保証するための強力なツールです。ロガーの例では、このパターンを適用してアプリケーション全体でログの一貫性を確保する方法を確認しました。
これが Golang のシングルトンをより深く理解するのに役立つことを願っています。