[Design pattern] 1-1. シングルトンパターン(Singleton pattern)
こんにちは。明月です。
この投稿はデザインパターンのシングルトンパターン(Singleton pattern)に関する説明です。
シングルトンパターンはデザインパターンの中で一番有名なパターンです。デザインパターンを聞いたことがなくてもシングルトンパターン(Singleton pattern)を聞いたことがあるほど有名なパターンです。
シングルトンパターンはクラスのインスタンスをプログラム実行中で一回だけ生成して続けて再使用するパターンです。利点ではクラスのデータを変わらずにずっと使うかすべてのオブジェクトからデータを共有しなければならない状況で使います。
C/C++例から確認しましょう。
// 重複検査前処理文
#pragma once
// ヘッダー宣言
#include <stdio.h>
#include <iostream>
using namespace std;
// クラス宣言
class Node
{
private:
// シングルトン変数設定
static Node* singleton;
// コンストラクタをprivateに設定して外部から生成禁止
Node() {}
protected:
public:
// インスタンスを取得する関数
static Node* getInstance() {
// シングルトン変数がnullなら
if (singleton == nullptr) {
// メモリ割り当て(クラス内部なのでインスタンス生成が可能)
singleton = new Node();
}
// ポインタ返却
return singleton;
}
// 出力関数
void print() {
// 画面出力
cout << "hello world" << endl;
}
};
// シングルトンの変数の設定を初期化
Node* Node::singleton = nullptr;
// 実行関数
int main()
{
// インスタンス取得
Node* node1 = Node::getInstance();
// インスタンス取得
Node* node2 = Node::getInstance();
// 関数実行
node1->print();
// メモリのアドレスを出力
printf("%d\n", node1);
// メモリのアドレスを出力
printf("%d\n", node2);
// Nodeインスタンスのメモリ解除
delete Node::getInstance();
return 0;
}
シングルトンの特性はコンストラクタをprivateに設定することが重要です。コンストラクタをprivateに設定するとクラス外部からインスタンス生成をできません。
そしてクラスのインスタンスはプログラムが終了する時まで保持しなければならないので、staticで宣言してクラス内部で管理します。
また、static関数(getInstance())でクラスのインスタンスを生成してsingleton変数に管理すればインスタンスを一回生成して再使用するシングルトンが作られます。
上の結果をみればnode1とnode2のメモリアドレスが同じです。
つまり、Node::getInstance()でインスタンスを取得すれば同じクラスが返却します。
// 実行関数があるクラス
public class Program {
// 実行関数
public static void main(String[] args) {
// Nodeインスタンス取得
Node node1 = Node.getInstance();
// Nodeインスタンス取得
Node node2 = Node.getInstance();
// 関数実行
node1.print();
// node1インスタンスのメモリアドレスを出力
System.out.println(node1.hashCode());
// node2インスタンスのメモリアドレスを出力
System.out.println(node2.hashCode());
}
}
// Nodeクラス
class Node {
// シングルトン変数設定
private static Node singleton;
// コンストラクタをprivateに設定、つまり外部クラスにはインスタンス生成ができない
private Node() {}
// インスタンス取得関数
public static Node getInstance() {
// シングルトン変数がnullならインスタンスを生成
if (singleton == null) {
// インスタンスを生成
singleton = new Node();
}
// インスタンス返却
return singleton;
}
// 出力関数
public void print() {
// コンソール出力
System.out.println("Hello world");
}
}
Javaのシングルトンパターンもコンストラクタをprivateに設定してクラスインスタンスをもっている変数をprivate staticで宣言します。
つまり、プログラムが終了する時まで変数のインスタンスが保持されます。
Javaのhashcodeを出力値をみれば同じ値が出力することを確認できます。
つまり、同じクラスという意味です。
using System;
namespace TestExample1
{
// Nodeクラス
class Node
{
// シングルトン変数設定
private static Node singleton;
// コンストラクタをprivateに設定、つまり外部クラスにはインスタンス生成ができない
private Node() { }
// インスタンス取得関数
public static Node GetInstance()
{
// シングルトン変数がnullならインスタンスを生成
if (singleton == null)
{
// インスタンスを生成
singleton = new Node();
}
// インスタンス返却
return singleton;
}
// 出力関数
public void Print()
{
// コンソール出力
Console.WriteLine("Hello world");
}
}
// 実行関数があるクラス
class Program
{
// 実行関数
static void Main(string[] args)
{
// Nodeインスタンス取得
Node node1 = Node.GetInstance();
// Nodeインスタンス取得
Node node2 = Node.GetInstance();
// 関数実行
node1.Print();
// node1インスタンスのメモリアドレスを出力
Console.WriteLine(node1.GetHashCode());
// node2インスタンスのメモリアドレスを出力
Console.WriteLine(node2.GetHashCode());
Console.WriteLine("Press Any Key...");
Console.ReadKey();
}
}
}
C#にもコンストラクタをprivateに設定してGetInstance()関数を利用してインスタンスを取得します。
結果も同じHashCodeが出力することで同じクラスインスタンスということを確認できます。
シングルトンパターンは普通リソースを扱うクラスでよく使います。
例えば、File IOや通信Socketクラスと共に使います。
なぜなら一つのFileを読み込んで書き込むクラスを様々なインスタンスで生成して接続すればconnection errorが発生します。
ログシステムがその例です。ログクラスを様々なインスタンス生成してファイル書き込むすればエラーが発生します。
using System;
using System.IO;
using System.Text;
using System.Threading;
namespace TestExample1
{
// ログクラス例
class LogSystem
{
// ファイルにメッセージを作成する関数
public void Write(String msg)
{
// Streamを生成
using (var stream = new FileStream("d:\\work\\log.log", FileMode.Append, FileAccess.Write))
{
// StringをUTF8タイプに変換
var b = Encoding.UTF8.GetBytes(msg);
// ファイルにメッセージを作成
stream.Write(b, 0, b.Length);
}
}
}
// 実行関数があるクラス
class Program
{
// 実行関数
static void Main(string[] args)
{
// スレッド
ThreadPool.QueueUserWorkItem((_) =>
{
// ログクラス例のインスタンス生成
var log = new LogSystem();
// 0から99まで
for (int i = 0; i < 100; i++)
{
// メッセージ作成
log.Write(i.ToString() + "\r\n");
}
});
// スレッド
ThreadPool.QueueUserWorkItem((_) =>
{
// ログクラス例のインスタンス生成
var log = new LogSystem();
// 0から99まで
for (int i = 0; i < 100; i++)
{
// メッセージ作成
log.Write(i.ToString() + "\r\n");
}
});
Console.WriteLine("Press Any Key...");
Console.ReadKey();
}
}
}
一つのクラスですが、各のインスタンスを生成するとIOExceptionが発生します。
ソケットも一つのインスタンスでportを開いて待機中ですが、別のインスタンスで接続すればport使用中というエラーが発生します。
その状況を解けるために一つのインスタンスを保持しなければならないですが、その時にシングルトンパターンを使うと良いです。
ここまでデザインパターンのシングルトンパターン(Singleton pattern)に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [Design pattern] 3-2. 責任の連鎖パターン(Chain of responsibility pattern)2021/11/04 19:27:58
- [Design pattern] 3-1. ストラテジーパターン(Strategy pattern)2021/11/03 18:38:52
- [Design pattern] 2-7. ファサードパターン(Facade pattern)2021/11/02 19:32:31
- [Design pattern] 2-6. プロキシパターン(Proxy pattern)2021/11/01 19:42:44
- [Design pattern] 2-5. フライウェイトパターン(Flyweight pattern)2021/10/29 19:48:27
- [Design pattern] 2-4. デコレーターパターン(Decorator pattern)2021/10/28 20:11:13
- [Design pattern] 2-3. ブリッジパターン(Bridge pattern)2021/10/27 20:32:21
- [Design pattern] 2-2. コンポジットパターン(Composite pattern)2021/10/27 20:30:54
- [Design pattern] 2-1. アダプターパターン(Adapter pattern)2021/10/26 19:12:40
- [Design pattern] 1-5. プロトタイプパターン(Prototype pattern)2021/10/22 19:35:45
- [Design pattern] 1-4. デザインパターンの抽象ファクトリーパターン(Abstract factory pattern)2021/10/15 19:31:03
- [Design pattern] 1-3. ファクトリーメソッドパターン(Factory method pattern)2021/06/23 19:45:37
- [Design pattern] 1-2. ビルダーパターン(Builder pattern)2021/06/11 19:06:28
- [Design pattern] 1-1. シングルトンパターン(Singleton pattern)2021/06/09 19:40:05
- [Design Pattern] デザインパターンの紹介2021/06/08 20:42:36
- check2024/04/10 19:03:53
- [Java] 64.Spring bootとReactを連結する方法(Buildする方法)2022/03/25 21:02:18
- [Javascript] Node.jsをインストールしてReactを使う方法2022/03/23 18:01:34
- [Java] 63. Spring bootでcronスケジューラとComponentアノテーション2022/03/16 18:57:30
- [Java] 62. Spring bootでWeb-Filterを設定する方法(Spring Security)2022/03/15 22:16:37
- [Java] JWT(Json Web Token)を発行、確認する方法2022/03/14 19:12:58
- [Java] 61. Spring bootでRedisデータベースを利用してセッションクラスタリング設定する方法2022/03/01 18:20:52
- [Java] 60. Spring bootでApacheの連結とロードバランシングを設定する方法2022/02/28 18:45:48
- [Java] 59. Spring bootのJPAでEntityManagerを使い方2022/02/25 18:27:48
- [Java] 58. EclipseでSpring bootのJPAを設定する方法2022/02/23 18:11:10
- [Java] 57. EclipseでSpring bootを設定する方法2022/02/22 19:04:49
- [Python] Redisデータベースに接続して使い方2022/02/21 18:23:49
- [Java] Redisデータベースを接続して使い方(Jedisライブラリ)2022/02/16 18:13:17
- [C#] Redisのデータベースを接続して使い方2022/02/15 18:46:09
- [CentOS] Redisデータベースをインストールする方法とコマンドを使い方2022/02/14 18:33:07