まさるblog

越後在住子持ちプログラマー奮闘記 - Author:まさる(高野 将、TAKANO Sho)

目次

Blog 利用状況

ニュース

著書

2010/7発売


Web掲載記事

@IT

.NET開発を始めるVB6プログラマーが知るべき9のこと

CodeZine

実例で学ぶASP.NET Webフォーム業務アプリケーション開発のポイント

第1回 3層データバインドを正しく活用しよう(前編)

ブログパーツ


書庫

日記カテゴリ

コミュニティ

デザインパターンを学ぶ~その18:Singletonパターン(1)~

Singleton (シングルトン)
 一人っ子、単一のこと・もの

Singletonパターンは、インスタンスがひとつしかない唯一のオブジェクトを生成し、そのインスタンスを取得する方法を公開します。

例えば、アプリケーション実行時のログを出力するような場合を考えます。ログのファイル名をシステム日付+時刻でつけるとしましょう。ログ出力を初回に行う際に、ログファイル名を決定し、以後はそのログファイルにログを出力します。

この場合、ログファイル名はアプリケーション実行時に1つだけでなくてはなりません。このような場合、Singletonが使えます。

 

以下に、コード例を示します。

C#
/// <summary>
/// ログ出力クラス
/// </summary>
public class SingletonLogger
{
  /// <summary>
  /// ログ出力パス
  /// </summary>
  private string _logPath;

  /// <summary>
  /// プロパティ ログ出力パス
  /// </summary>
  public string LogPath
  {
    get
    {
      return _logPath;
    }
  }

  /// <summary>
  /// コンストラクタ
  /// </summary>
  /// <remarks>new でインスタンス化できないよう、privateでコンストラクタを定義</remarks>
  private SingletonLogger()
  {
    _logPath = DateTime.Now.ToString("yyyyMMddhhmmssfff") + ".log";
  }

  /// <summary>
  /// 自身の唯一のインスタンス
  /// </summary>
  private static SingletonLogger _uniqueLogger;

  /// <summary>
  /// プロパティ インスタンス
  /// </summary>
  /// <remarks>自身の唯一のインスタンスを返す</remarks>
  public static SingletonLogger Instance
  {
    get
    {
      if ( _uniqueLogger == null )
      {
        _uniqueLogger = new SingletonLogger();
      }
      return _uniqueLogger;
    }
  }

  /// <summary>
  /// ログ出力
  /// </summary>
  /// <param name="message"></param>
  public void WriteLog(string message)
  {
    // ログ出力処理
    // ・・・
  }
}
VB
''' <summary>
''' ログ出力クラス
''' </summary>
''' <remarks></remarks>
Public Class SingletonLogger

  ''' <summary>
  ''' ログ出力パス
  ''' </summary>
  ''' <remarks></remarks>
  Private _logPath As String

  ''' <summary>
  ''' プロパティ ログ出力パス
  ''' </summary>
  ''' <value></value>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public ReadOnly Property LogPath() As String
    Get
      Return _logPath
    End Get
  End Property

  ''' <summary>
  ''' コンストラクタ
  ''' </summary>
  ''' <remarks>new でインスタンス化できないよう、privateでコンストラクタを定義</remarks>
  Private Sub New()
    _logPath = DateTime.Now.ToString("yyyyMMddhhmmssfff") & ".log"
  End Sub

  ''' <summary>
  ''' 自身の唯一のインスタンス
  ''' </summary>
  ''' <remarks></remarks>
  Private Shared _uniqueLogger As SingletonLogger

  ''' <summary>
  ''' プロパティ インスタンス
  ''' </summary>
  ''' <value></value>
  ''' <returns></returns>
  ''' <remarks>自身の唯一のインスタンスを返す</remarks>
  Public Shared ReadOnly Property Instance() As SingletonLogger
    Get
      If _uniqueLogger Is Nothing Then
        _uniqueLogger = New SingletonLogger
      End If
      Return _uniqueLogger
    End Get
  End Property

  ''' <summary>
  ''' ログ出力
  ''' </summary>
  ''' <param name="message"></param>
  Public Sub WriteLog(ByVal message As String)
    ' ログ出力処理
    ' ・・・
  End Sub

End Class

このコードのポイントは以下の3点です。

  • コンストラクタをprivateとして、外からはインスタンス化できないようにする。
  • privateな静的フィールドとして、自身の型のフィールドを用意し、クライアントにはそれを提供するようにする。
  • Instanceプロパティ内で、上記静的フィールドがnullの場合のみ、新たにインスタンス化するようにし、唯一のオブジェクトを保障する。

 

以上のコードを使って見ましょう。

C#
public class Program
{
  static void Main(string[] args)
  {
    {
      var logger = SingletonLogger.Instance;
      Console.WriteLine(logger.LogPath);
    }

    System.Threading.Thread.Sleep(1000);

    {
      var logger = SingletonLogger.Instance;
      Console.WriteLine(logger.LogPath);
    }

    Console.ReadKey();
  }
}
VB
Public Class Program

  Public Shared Sub Main()

    With Nothing
      Dim logger = SingletonLogger.Instance
      Console.WriteLine(logger.LogPath)
    End With

    System.Threading.Thread.Sleep(1000)

    With Nothing
      Dim logger = SingletonLogger.Instance
      Console.WriteLine(logger.LogPath)
    End With

    Console.ReadKey()

  End Sub

End Class

実行結果

20080608125533068.log
20080608125533068.log

 

実行結果を見るとわかるように、1つのインスタンスが使いまわされていることがわかります。

 

さて、以上のコードは、通常の使用方法では問題がないのですが、以下のようにマルチスレッドで動かす場合、1つだけであるはずのインスタンスが、複数生成されてしまうことがあります。

C#
/// ログ出力クラス
/// </summary>
public class SingletonLogger
{
  ・・・

  /// <summary>
  /// コンストラクタ
  /// </summary>
  /// <remarks>new でインスタンス化できないよう、privateでコンストラクタを定義</remarks>
  private SingletonLogger()
  {
    // 重い初期化処理
    for ( var i = 0; i < 100000000; i++ )
    {
    }

    _logPath = DateTime.Now.ToString("yyyyMMddhhmmssfff") + ".log";
  }

  ・・・
}

public class Program
{
  static void Main(string[] args)
  {
    SingletonLogger logger1 = null;
    var thread1 = new Thread(
      () =>
      {
        logger1 = SingletonLogger.Instance;
      }
    );
    SingletonLogger logger2 = null;
    var thread2 = new Thread(
      () =>
      {
        logger2 = SingletonLogger.Instance;
      }
    );

    thread1.Start();
    thread2.Start();

    while ( logger1 == null || logger2 == null)
    {
      Thread.Sleep(10);
    }

    Console.WriteLine(logger1.LogPath);
    Console.WriteLine(logger2.LogPath);

    Console.ReadKey();
  }
}
VB
''' <summary>
''' ログ出力クラス
''' </summary>
''' <remarks></remarks>
Public Class SingletonLogger

  ・・・

  ''' <summary>
  ''' コンストラクタ
  ''' </summary>
  ''' <remarks>new でインスタンス化できないよう、privateでコンストラクタを定義</remarks>
  Private Sub New()
    ' 重い初期化処理
    For i As Integer = 0 To 100000000
    Next i

    _logPath = DateTime.Now.ToString("yyyyMMddhhmmssfff") & ".log"
  End Sub

  ・・・

End Class

Public Class Program

  Public Shared Sub Main()

    Dim loggerHelper1 As New LoggerHelper()
    Dim thread1 = New Thread(AddressOf loggerHelper1.SetLogger)

    Dim loggerHelper2 As New LoggerHelper()
    Dim thread2 = New Thread(AddressOf loggerHelper2.SetLogger)

    thread1.Start()
    thread2.Start()

    While (loggerHelper1.Logger Is Nothing OrElse loggerHelper2.Logger Is Nothing)
      Thread.Sleep(10)
    End While

    Console.WriteLine(loggerHelper1.Logger.LogPath)
    Console.WriteLine(loggerHelper2.Logger.LogPath)

    Console.ReadKey()

  End Sub

  Public Class LoggerHelper

    Public Logger As SingletonLogger

    Public Sub SetLogger()
      Me.Logger = SingletonLogger.Instance
    End Sub
  End Class

End Class

実行結果

20080608011949432.log
20080608011949613.log

ごらんのように、インスタンスが2つ生成された結果、ログ出力パスが変わってしまいます。

この問題を解消する方法はないのでしょうか?

 

実はあります。このあたり、次回のエントリで取り上げようと思います。

 

#今回から、.NET Framework 3.5 のコードにしています。

投稿日時 : 2008年6月8日 1:24

Feedback

# re: デザインパターンを学ぶ~その18:Singletonパターン(1)~ 2008/06/08 10:53 さかもと

なるほどー。
分かりやすかったー。
次回も期待・・・。

# re: デザインパターンを学ぶ~その18:Singletonパターン(1)~ 2008/06/08 13:42 まさる

>さかもと画伯
コメントありがとうございます。

わかりやすいといってもらえてうれしい限りです。
ただ、コードの説明をぜんぜんしていなかったので、ちょいと修正しました。

# 【お詫びと訂正】デザインパターンを学ぶ~その18:Singletonパターン(1)~ 2008/07/17 22:47 まさるblog

【お詫びと訂正】デザインパターンを学ぶ~その18:Singletonパターン(1)~

# デザインパターンを学ぶ~その19:Singletonパターン(2)~ 2008/07/24 23:24 まさるblog

デザインパターンを学ぶ~その19:Singletonパターン(2)~

# トートバッグ ブランド激安 コピー 2017/06/19 15:25 hooksc@ezweb.ne.jp

2017年の超人気ブランド新品
商品数も大幅に増え、品質も大自信です
当社の商品は絶対の自信が御座います
品質がよい 価格が低い 実物写真 品質を重視
財布コピー、バッグコピー、腕時計コピー、小物コピー、
N品、価格激安、品質の保証,
ご注文を期待しています
トートバッグ ブランド激安 コピー http://www.newkokoku.com

# エルメス最高品質 2017/07/16 1:23 tvepyzrg@icloud.com

激安ブランド直営店
1.最も合理的な価格で商品を消費者に提供致します。
2.弊社の商品品数大目で、商品は安めです!商品現物写真。
3.数量制限無し、一個の注文も、OKです。
4.1個も1万個も問わず、誠心誠意対応します。
5.不良品の場合、弊社が無償で交換します。不明点、疑問点等があれば、ご遠慮なく言って下さい。
以上よろしくお願いいたします
休業日: 365天受付年中無休

# 韓国コピー時計 2017/09/26 6:04 jyjfsfaxyur@softbank.jp

迅速に対応していただき、また包装もキレイでとても良かったです。
また、何かの機会がありましたら、利用させていただきます。
【送料無料】グッチ パスケースをセール価格で販売中♪グッチ パスケース グッチスウィング トレインパスケース 354500 ライトブルー レザー 新品 未使用 カードケース トレードマーク GUCCI
通勤用
同じ商品の定期入れを落としてしまいました。定期券・いろんなカードが入っていて、気に入って使用していたのですが、落とし物でどこにも届いておらず、残念です(><@)
また、色違いで購入しました。
色は迷いましたが、キレイなブルーに決めました。
これで一気進展、愛用し頑張りたいと思います。

# 偽物 萬富 2017/10/31 6:33 gldblguxx@yahoo.co.jp

看板に偽りなし、本当に新品同様でした。配送も早かったです。また、手書きの一筆箋を同封いただき、心がこもった対応だと感じました。
☆新品同様!! 【送料無料】★ルイヴィトン★タイガ★ポルトバルール・カルトクレディ★ニつ折り長札入れ★M30392★
かなりじろじろ見まわしましたが、全く使用感が無く文字通り新品同様でした。何故こんな安く買えてしまったのでしょうか。ラッキーとしか言いようがありません。
偽物 萬富 http://www.yamamo78.com/web/watch-fr004.htm

# A品ルイヴィトン 2017/11/26 12:33 arxmipkxs@aol.jp

栃木のおすすめのおみやげは御用邸チーズケーキです。
那須のチーズガーデンで作られているこのチーズケーキはベイクドタイプ。
新鮮な上質のクリームチーズを数種、オリジナルレシピでブレンドし、熟練のパティシエが3段階の温度で焼くこのチーズケーキ。
ムラのない美しい焼き色が綺麗です。
とっても濃厚で、パサつきはまず感じられないほどのしっとりなめらかな食感、なめらかな舌触り。
口に入れると甘酸っぱさとチーズの香りが鼻に抜けていき、舌の上で溶けていきます。

# qCnjGNmlkQobmYP 2021/07/03 2:15 https://amzn.to/365xyVY

You have made some decent points there. I checked on the web to learn more about the issue and found most people will go along with your views on this web site.

# ロレックス 中古 無金利 2023/11/10 20:25 xxpgln@livedoor.com

ブランド偽物(コピー商品)激安市場
バッグ、財布、腕時計人気貴族店
2023ブランド偽物バッグ財布時計コピー激安
★最高級のバッグ 財布 時計大量入荷超人気!!!★
超人気のブランド店実物写真ブランド店
■2023年の超人気ブランド新品■
┏━━━━豪華贅沢品━━━━┓
◎━バッグ :ブラダ、イースト、アウトドア、コーチ、ディーゼル
◎━財 布:カルティエ、ブルガリ、グッチ、ブラダ、コーチ
◎━時 計:ロレックス、パネライ、シャネル
■◎━━◎低価格 低価格◎━━◎■
■◎━━◎年末引割り◎━━◎ ■
☆●●●●●運賃無料実施中●●●●●☆
↓↓↓
ロレックス 中古 無金利 https://www.b2tokei.com/product/detail.aspx-id=3745.htm

タイトル
名前
Url
コメント