japan.internet.comThe Internet & IT Network
Twitter
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
2010年2月10日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
あなたが最も利用しているのはどれですか?
SNS
Blog
Twitter
掲示板
投票締切 2/15 12:00
デベロッパー2009年4月21日 10:00

スマートなアプリケーションアーキテクチャの構築(1)

海外海外internet.com発の記事
  • Post to Twitter
  • Post to Facebook
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 この連載では、ほぼすべてのサイズのプログラムで利用できる簡単なアプリケーションアーキテクチャの作成について見ていきます。記事中で使用するコードは概念を説明するためのものであり、実際に使用するためのものではありません。

パート1:データに知能を持たせる

 すべてのアプリケーションは一連のオブジェクトからなり、これらオブジェクトの多くはプロパティ群を公開しており、外側から操作できるようにしています。データメンバーを直接公開するのではなくプロパティを使用するのには、2つの利点があります。1つめはgetter/setter内に暗黙の操作を実装できること、2つめは、暗黙の操作を実装しない場合でも、リフレクションなどのツール向けに共通の基準を提供できることです。まずは、インボイス(請求明細書)を表す簡単なクラス群を考えてみましょう。

public class Invoice
   {
      Invoice()
      {
         Items = new List<InvoiceLineItem>();
      }
      public Guid InvoiceID       { get; set; }
      public string InvoiceNumber { get; set; }
      public string InvoiceDate   { get; set; }

      public ICollection<InvoiceLineItem> Items { get; private set; }
      public decimal ItemsTotalPrice  { get; set; }
      public decimal ItemsTotalTax    { <get; set; }
      public decimal ItemsTotalWeight { <get; set; }
      public decimal Shipping         { get; set; }
      public decimal ShippingRate     { get; set; }
      public decimal TaxRate          { get; set; }
      public decimal InvoiceTotal     { get; set; }
   }

   public class InvoiceLineItem
   {
      public Guid ItemID           { get; set; }
      public Guid InvoiceID        { get; set; }
      public int LineNumber        { get; set; }
      public decimal Quantity      { get; set; }
      public bool Taxable          { get; set; }
      public decimal UnitPrice     { get; set; }
      public decimal Weight        { get; set; }
      public decimal ExtendedPrice { get; set; }
   }
 .NET 2.0の自動プロパティ機能を使用すれば、get/set操作の仕組みを手動で実装する必要はなくなります。ただしこれを省略できる代償として、get/set操作に任意のロジックを格納できなくなります。getまたはsetのいずれかに操作を実装したい場合は、以下の形に戻る必要があります。

private decimal m_ExtendedPrice;
public decimal ExtendedPrice
{
   get { return m_ExtendedPrice; }
   set { m_ExtendedPrice = value; }
}
 圧縮形式にもかかわらず、1つのプロパティで6行にもなりました。これでは現在19行で済むところ、プロパティだけで110行以上にもなり、ソースコードが肥大化してしまいます。そこでコードを削るため、プロパティの構文をカプセル化した簡易クラスを作成します。

public class Field<DATA_TYPE>
   {
      private DATA_TYPE m_Value;
      public  DATA_TYPE Value
      {
         get { return m_Value; }
         set { m_Value = value; }
      }
   }
 コードを(ひとまず)簡略化するために、このヘルパーを使用して、プロパティではなく読み取り専用フィールドを持つようにInvoiceクラスを再実装します。

public class Invoice
{
   Invoice()
   {
      Items = new List<InvoiceLineItem>();
   }
   public readonly Field<Guid> InvoiceID = new Field<Guid>();
   public readonly Field<string> InvoiceNumber = new Field<string>();
   public readonly Field<string> InvoiceDate = new Field<string>();

   public ICollection<InvoiceLineItem> Items { get; private set; }
   public readonly Field<decimal> ItemsTotalPrice = new Field<decimal>();

   public readonly Field<decimal> ItemsTotalTax = new Field<decimal>();
   public readonly Field<decimal> ItemsTotalWeight = new Field<decimal>();
   public readonly Field<decimal> Shipping = new Field<decimal>();
   public readonly Field<decimal> ShippingRate = new Field<decimal>();
   public readonly Field<decimal> TaxRate = new Field<decimal>();
   public readonly Field<decimal> InvoiceTotal = new Field<decimal>();
}

public class InvoiceLineItem
{
   public readonly Field<Guid> ItemID = new Field<Guid>();
   public readonly Field<Guid> InvoiceID = new Field<Guid>();
   public readonly Field<int> LineNumber = new Field<int>();
   public readonly Field<decimal> Quantity = new Field<decimal>();
   public readonly Field<bool> Taxable = new Field<bool>();
   public readonly Field<decimal> UnitPrice = new Field<decimal>();
   public readonly Field<decimal> Weight = new Field<decimal>();
   public readonly Field<decimal> ExtendedPrice = new Field<decimal>();
}
 この場合、情報を設定または検索する際には、必ずフィールドのValueプロパティを使用する必要があります。一見したところ複雑化してクラスの宣言や使用がしにくくなったように見えますが、Fieldクラスの機能を拡張していくにつれ大きな利点を見出すことになります。まずは、Fieldインスタンスに検証機能と代入機能を追加します。この例では、まず基本クラスを作成し、次にdecimal型の値が範囲内に収まることを確認する具体的な検証クラスを作成します。

public class Validator<DATA_TYPE>
{
   public virtual void Validate(DATA_TYPE value) { return; }
}

public class RangeValidator : Validator<decimal>
{
   public RangeValidator(decimal min, decimal max)
   {
      m_Min = min;
      m_Max = max;
   }
   public override void Validate(decimal value)
   {
      if ((value < m_Min) || (value > m_Max))
      throw new ArgumentException();
   }

   private readonly decimal m_Min;
   private readonly decimal m_Max;
}
 ここまでできたら、次はFieldクラスを拡張してValidatorを認識させます。

public class Field<DATA_TYPE>
{
   public Field()
   {
      m_Validator = new Validator<DATA_TYPE>();
   }
   public Field(Validator<DATA_TYPE> validator)
   {
      m_Validator = validator;
   }

   private DATA_TYPE m_Value;
   public DATA_TYPE Value
   {
      get { return m_Value; }
      set
      {
         m_Validator.Validate(value);
         m_Value = value;
      }
   }

   private readonly Validator<DATA_TYPE> m_Validator;
}
 これで、すべてのフィールドに検証機能を追加することができます。

public class InvoiceLineItem
{
   public readonly Field<Guid> ItemID = new Field<Guid>();
   public readonly Field<Guid> InvoiceID = new Field<Guid>();
   public readonly Field<int> LineNumber = new Field<int>();
   public readonly Field<decimal> Quantity =
      new Field<decimal>(new RangeValidator(1, 100));
   public readonly Field<bool> Taxable = new Field<bool>();
   public readonly Field<decimal> UnitPrice =
      new Field<decimal>(new RangeValidator(0.01M, 9999M));
   public readonly Field<decimal> Weight =
      new Field<decimal>(new RangeValidator(0.1M, 99M));
   public readonly Field<decimal> ExtendedPrice =
      new Field<decimal>();
}
 指定した範囲外の値を代入しようとすると、自動的にArgument Exception(引数例外)を返すようになりました。次に、Valueの変更に対してクラスオブジェクトが自動で反応するようにします。ここでも、Fieldクラスを拡張することでこの機能を実装します。

public class Field<DATA_TYPE>
{
   public class ValueChangedEventArgs : EventArgs
   {
      internal ValueChangedEventArgs(DATA_TYPE oldValue,
                                     DATA_TYPE newValue)
      {
         OldValue = oldValue;
         NewValue = newValue;
      }

      public readonly DATA_TYPE OldValue;
      public readonly DATA_TYPE NewValue;
   }

   public event EventHandler<ValueChangedEventArgs> ValueChanged;
   private void Fire_ValueChanged(DATA_TYPE oldValue,
                                  DATA_TYPE newValue)
   {
      if (ValueChanged != null)
          ValueChanged(this,
             new ValueChangedEventArgs(oldValue, newValue));
   }
   public DATA_TYPE Value
   {
      get { return m_Value; }
      set
      {
         if (!Equals(m_Value, value))
        {
            DATA_TYPE oldValue = m_Value;
            m_Validator.Validate(value);
            m_Value = value;
            Fire_ValueChanged(oldValue, m_Value);
        }

      }
   }
}
 この機能は、クラスに「ビジネスロジック」を含めるようになった段階で使用することができます。

public class Field<DATA_TYPE>
{
   public class ValueChangedEventArgs : EventArgs
   {
      internal ValueChangedEventArgs(DATA_TYPE oldValue,
                                     DATA_TYPE newValue)
      {
         OldValue = oldValue;
         NewValue = newValue;
      }

      public readonly DATA_TYPE OldValue;
      public readonly DATA_TYPE NewValue;
   }

   public event EventHandler<ValueChangedEventArgs> ValueChanged;
   private void Fire_ValueChanged(DATA_TYPE oldValue,
                                  DATA_TYPE newValue)
   {
      if (ValueChanged != null)
                 ValueChanged(this,
                 new ValueChangedEventArgs(oldValue, newValue));
   }
   public DATA_TYPE Value
   {
      get { return m_Value; }
      set
      {
         if (!Equals(m_Value, value))
        {
           DATA_TYPE oldValue = m_Value;
           m_Validator.Validate(value);
           m_Value = value;
           Fire_ValueChanged(oldValue, m_Value);
        }

      }
   }
}

まとめ

 ここまでで、検証機能とイベント駆動ロジックの両方を実装する簡単なアーキテクチャができました。これはほんの始まりに過ぎません。次回は、クラスレベルに同様の原則を適用して、インテリジェントなエンティティやコレクションを生成します。今後の記事では、ユーザーインターフェース(UI)に対するデータのバインドやインテリジェントな永続性についても取り上げる予定です。

詳細情報

 本連載における情報は、Dynamic Concepts Development社発行のガイド『Smart Architecture』に基づいています。これはオープンなアーキテクチャであるとともに、開発者コミュニティで自由に利用できる推奨インターフェース群です。商企業は、このアーキテクチャを利用して実装やツールセットの開発を行うことができます。

著者紹介

TheCPUWizard(TheCPUWizard)
 高性能かつ高信頼性のソフトウェアシステム開発に30年の経験を持つシニアソフトウェアアーキテクト。
  • プリンター用
  • 記事を転送
  • Post to Twitter
  • Post to Facebook
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • BuzzurlにブックマークBuzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート
最新トップニュース
【次回予告】 2010年3月9日(火) >>詳細はこちら
第6回 インターネットコム・マーケティングセミナー
進化するモバイルマーケティング 〜最新手法と必要性〜
2009年12月16日(水)開催 10周年記念セミナー
報告レポートはこちら
データメーション
【データメーション】
非常に危険な中国人ハッカー(2月5日)
検索エンジンマーケティング
検索エンジンマーケティング
リンクとは、サイト間の支持票?(2月10日)
人が育つすごいしかけ
人が育つすごいしかけ
育成すべき社員は「能力」ではなく「○○○」で選ぶ?(2月10日)
「IT の耳」
「IT の耳」
【書評】ソーシャルメディアマーケティング(2月10日)
e-Japan 先端テクノロジー解説
e-Japan 先端テクノロジー解説
自治体クラウド開発実証事業の成功要件を検討する(2月10日)
百式のネットビジネス研究
百式のネットビジネス研究
iPhone アプリ制作会社が一覧できる「They Make Apps」(2月9日)
CodeGuru
CodeGuru
C#におけるNull Objectパターン(2月9日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
日本を元気に!“位置ゲー”のコロプラが止まらない(2月9日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
あなたのサイトの体調管理は万全ですか?(2月9日)
ソフトウェア研究最前線
ソフトウェア研究最前線
ソフトウェア開発におけるインタラクションデザイン(2)(2月8日)
生活者の力をマーケティングに活かそう
生活者の力をマーケティングに活かそう
顧客にラブレターを書くために必要なこと(2月8日)
Copyright 2010 internet.com K.K. (Japan) All Rights Reserved.