Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

C#におけるNull Objectパターン

クライアントコードを簡素化し、誤りを防ぐNull Objectパターンの実装方法

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2010/01/15 14:00

 Null Objectパターンはクライアントコードを簡素化し、誤りを生じにくくするのに役立ちます。本稿では、簡単な販売アプリケーションのサンプルプログラムを作成しながら、簡潔で理解しやすいNull Objectパターンの実装方法について説明していきます。

はじめに

 Nullオブジェクトとは、オブジェクトが存在しないことを表すオブジェクトです。何の動作も行わずに、デフォルトを返します。Nullオブジェクトは、オブジェクト参照がNullである場合に使われます。Null Objectパターンはクライアントコードを簡素化し、誤りを生じにくくするのに役立ちます。

 例えば、顧客アカウントを確認し、顧客がゴールド会員であるかどうかをチェックする簡単な販売アプリケーションを考えてみましょう。ゴールド会員は、割引や配送料無料など、さまざまな特典を受けることができるものとします。

 以下は、mainメソッドのコードスニペットです。顧客のアカウント情報を取得し、そのゴールド会員プロファイルを取り出し、そのゴールド会員に適用されるすべての特典を適用します。

string userName = args[0];
string pin = args[1];
Order order = new Order();

for (int i = 2; i < args.Length; i++)
{
    order.Amount += Convert.ToDecimal(args[i]);
}

var account = AccountController.GetAccount(userName, pin);

if (account == null)
{
    Console.WriteLine("Invalid Account");
}
else
{
    var gProfile = GoldMembershipController.GetProfile(account);

    if (gProfile != null)
    {
        order.Amount -= gProfile.GetDiscount(order.Amount);
    }

    if (gProfile != null)
    {
        if (!gProfile.IsShippingFree)
        {
            order.Amount += order.GetShippingCharges();
        }
    }
    else
    {
        order.Amount += order.GetShippingCharges();
    }

    Console.WriteLine(string.Format("Total Amount: {0}", order.Amount));
}

 顧客が有効なアカウントを所有する場合、AccountController.GetAccount(userName, pin)Accountオブジェクトを返し、それ以外の場合はnullを返します。同様に、顧客がゴールド会員であれば、GoldMembershipController.GetProfile(account)GoldMembershipオブジェクトを返し、ゴールド会員でない場合はnullを返します。

 上のコードスニペットから分かるように、null参照をチェックする条件文が何度も繰り返し出現します。条件文は通常、条件結果に基づいて分岐するために使用します。上のゴールド会員であるかをチェックする部分では、同じフローの中で、null参照をチェックするためだけに2つの条件文が使用されています。これでは、コードが長くなって理解しにくくなるだけでなく、大きなプログラムではnullのチェックを忘れてしまいがちであるため、非常に誤りが生じやすくなります。Null Objectパターンは、このような問題を解決するためのものです。

 Null Objectパターンを実装すると、GoldMembershipController.GetProfile(accountId)は常にオブジェクトを返すことになります。顧客がゴールド会員ではない場合には、Nullオブジェクトを返します。Null Objectパターンを実装するには、IGoldMembershipインターフェースを作成し、GoldMembershipクラスとNullGoldMembershipクラスの両方でこのインターフェースを実装します。NullGoldMembershipクラスは、何も行わずにデフォルトを返すだけのクラスです。GoldMembershipController.GetProfile(accountId)メソッドも変更し、ユーザーがゴールド会員である場合はGoldMembershipオブジェクト、ゴールド会員でない場合はNullGoldMembershipオブジェクトを返すようにします。

public interface IGoldMembership
    {
        decimal GetDiscount(decimal amount);
        bool IsShippingFree { get; set; }
    }

public class GoldMembership : IGoldMembership
    {
        public GoldMembership(string userName)
        {
            // get gold member profile of this user

            IsShippingFree = true;
        }

        public bool IsShippingFree { get; set; }

        public decimal GetDiscount(decimal amount)
        {
            decimal discount = 100;

            //calculate discount

            return discount;
        }
    }

public class NullGoldMembership : IGoldMembership
    {
        public NullGoldMembership()
        {
            IsShippingFree = false;
        }

        public decimal GetDiscount(decimal amount)
        {
            return 0;
        }

        public bool IsShippingFree { get; set; }
    }

public class GoldMembershipController
    {
        public static IGoldMembership GetProfile(Account account)
        {
            // if this account has gold membership
            // return profile else return null

            if (account.Type == "GoldMember")
            {
                return new GoldMembership(account.UserName);
            }
            else
            {
                return new NullGoldMembership();
            }
        }
    }

 Null Objectパターンを実装した後のmainメソッドのコードは、次のようになります。

string userName = args[0];
string pin = args[1];
Order order = new Order();

for (int i = 2; i < args.Length; i++)
{
    order.Amount += Convert.ToDecimal(args[i]);
}

var account = AccountController.GetAccount(userName, pin);

if (account == null)
{
    Console.WriteLine("Invalid Account");
}
else
{
    var gProfile = GoldMembershipController.GetProfile(account);

    order.Amount -= gProfile.GetDiscount(order.Amount);

  if (!gProfile.IsShippingFree)
  {
          order.Amount += order.GetShippingCharges();
  }

    Console.WriteLine(string.Format("Total Amount: {0}", order.Amount));
}

 Null Objectパターンを実装した後のクライアントサイドのコードは、かなりすっきりしたものになりました。簡潔であるだけでなく、理解しやすいものになっています。

 しかし、Null Objectパターンを使用すればnull参照を完全になくすことができるわけではありません。オブジェクトが存在するか否かによって分岐しなければならない場合もあり、そのような場合にはやはりnull参照を使用する必要があります。例えば、ユーザーが有効なアカウントを所有しない場合は、その後の処理は行わず、単にエラーメッセージを表示します。このような場合には、次のようにnull参照を使用し、チェックする必要があります。

var account = AccountController.GetAccount(userName, pin);

if (account == null)
{
    Console.WriteLine("Invalid Account");
}
else
{
    // process the transaction
}

 それでは、ハッピーコーディング!



  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

  • Sajad Deyargaroo(Sajad Deyargaroo)

    MCTS、MCP資格を取得。VB 4.0とC++のアプリケーション開発からこの世界に入る。プログラミングへの関心は数多くの言語にわたるが、現在は.Netに没頭している。最近は、AISとともにMicrosoftの技術に取り組んでおり、複数の雑誌およびWeb上に多くの記事を執筆している。電子メールアドレ...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5