SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

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

汎用クラスによるインスタンス群の一元管理

  • X ポスト
  • このエントリーをはてなブックマークに追加

Invoiceサンプルコードの拡張

 まずは、InvoiceクラスとInvoiceLineItemクラスを変更して、それぞれのクラスの基本クラスを利用するようにします。そのためには、さまざまなFieldオブジェクトの登録が必要です。

public class Invoice : Entity
{
   public Invoice()
   {
      RegisterField("InvoiceDate",      InvoiceDate);
      RegisterField("InvoiceID",        InvoiceID);
      RegisterField("InvoiceNumber",    InvoiceNumber);
      RegisterField("Items",            Items);
      RegisterField("ItemsTotalPrice",  ItemsTotalPrice);
      RegisterField("ItemsTotalTax",    ItemsTotalTax);
      RegisterField("ItemsTotalWeight", ItemsTotalWeight);
      RegisterField("Shipping",         Shipping);
      RegisterField("ShippingRate",     ShippingRate);
      RegisterField("TaxRate",          TaxRate);
      RegisterField("InvoiceTotal",     InvoiceTotal);
   }

   // Remainder of the Class is identical to previous version.....
}

public class InvoiceLineItem : Entity
{
   public Invoice()
   {
      RegisterField("InvoiceID",     InvoiceID);
      RegisterField("ItemID",        ItemID);
      RegisterField("LineNumber",    LineNumber);
      RegisterField("Quantity",      Quantity);
      RegisterField("Taxable",       Taxable);
      RegisterField("UnitPrice",     UnitPrice);
      RegisterField("Weight",        Weight);
      RegisterField("ExtendedPrice", ExtendedPrice);
   }

   // Remainder of the Class is identical to previous version.....
}

 続いて、特定のフィールドの変更に関係するすべてのフィールドを適切に更新するための一連のイベントを追加します。こうした関係は、インボイスについて回るものなので、クラスの内部に実装してオブジェクトの生成時に用意するようにします。

public class Invoice : Entity
{
   // Called from Constructor after registration
   private void InstallRules()
   {
      Items.CollectionChanged       += Items_CollectionChanged;
      ItemsTotalPrice.ValueChanged  += Recalculate_Tax;
      ItemsTotalTax.ValueChanged    += Recalculate_Total;
      ItemsTotalWeight.ValueChanged += Recalculate_Shipping;
      ItemsTotalTax.ValueChanged    += Recalculate_Total;
      ShippingRate.ValueChanged     += Recalculate_Shipping;
      TaxRate.ValueChanged          += Recalculate_Tax;
   }

   private void Items_CollectionChanged(object sender,
      CollectionChangedEventArgs e)
   {
      InvoiceLineItem lineItem =   e.Item as InvoiceLineItem;
      switch (e.ChangeType)
      {
         case CollectionChangedEventArgs.CollectionChangeTypes.Added:
            ItemsTotalPrice.Value  += lineItem.ExtendedPrice.Value;
            ItemsTotalWeight.Value += lineItem.Weight.Value;
            break;
         case CollectionChangedEventArgs.CollectionChangeTypes.Removed:
            ItemsTotalPrice.Value  -= lineItem.ExtendedPrice.Value;
            ItemsTotalWeight.Value -= lineItem.Weight.Value;
            break;
         case CollectionChangedEventArgs.CollectionChangeTypes.Cleared:
            ItemsTotalPrice.Value  = 0.0M;
            ItemsTotalWeight.Value = 0.0M;
            break;
      }
   }

   private void Recalculate_Tax(object sender,
      ValueChangedEventArgs<decimal> e)
   {
      ItemsTotalTax.Value = ItemsTotalPrice.Value * TaxRate.Value;
   }

   private void Recalculate_Shipping(object sender,
      ValueChangedEventArgs<decimal> e)
   {
      Shipping.Value =   ItemsTotalWeight.Value * ShippingRate.Value;
   }

   private void Recalculate_Total(object sender,
      ValueChangedEventArgs<decimal> e)
   {
      InvoiceTotal.Value = ItemsTotalPrice.Value
                         + ItemsTotalTax.Value
                         + Shipping.Value;
   }

   public void AddItem(InvoiceLineItem lineItem)
   {
      lineItem.LineNumber.Value = Items.Count + 1;
      Items.Add(lineItem.LineNumber.Value, lineItem);
      lineItem.ExtendedPrice.ValueChanged +=
         ExtendedPrice_ValueChanged;
   }

   private void ExtendedPrice_ValueChanged(object sender,
      ValueChangedEventArgs<decimal> e)
   {
      ItemsTotalPrice.Value += (e.NewValue - e.OldValue);
   }
}

 ようやくインボイスのクラスができました。非常にシンプルな実装によって、簡単なインボイスの情報を扱える論理演算がすべて実現されています。このコードは、メンテナンスもテストもきわめて容易です。

 使い方を示すために、サンプルプログラムを載せておきます。

private static void Main()
{
   Invoice invoice = new Invoice();
   invoice.InvoiceNumber.Value = "1000";
   invoice.TaxRate.Value = 0.07M;
   invoice.ShippingRate.Value = 1.23M;

   InvoiceLineItem invoiceLineItem = new InvoiceLineItem();
   invoiceLineItem.Quantity.Value = 10;
   invoiceLineItem.UnitPrice.Value = 19.95M;
   invoiceLineItem.Weight.Value = 0.75M;

   invoice.AddItem(invoiceLineItem);

   Console.WriteLine("Invoice Items Total: {0}",
                     invoice.ItemsTotalPrice.Value);
   Console.WriteLine("Invoice TAX: {0}",
                     invoice.ItemsTotalTax.Value);
   Console.WriteLine("Invoice Weight: {0}",
                     invoice.ItemsTotalWeight.Value);
   Console.WriteLine("Invoice Shipping: {0}",
                     invoice.Shipping.Value);
   Console.WriteLine("Invoice GRAND TOTAL: {0}",
                     invoice.InvoiceTotal.Value);
}

高度なトピック

 設計のこの段階で説明するには高度な内容ですが、フィールドのコーディングと登録を明示的に行う方法は他にもたくさんあることを知っておいてください。本格的な機能を実装すれば、リフレクションを使って適用可能なフィールドを自動で登録したり、要求された情報の提供に属性を使ったりできるでしょう。現在取り上げているシンプルなアーキテクチャが完成したら、こうした代替案についても説明したいと思います。

まとめ

 このシリーズの解説はまだ初期の段階ですが、最小限のコーディングで、れっきとしたアプリケーションを作成することができます。今後、数回にわたって拡張していけば、このInvoiceサンプルに対する(大きな)変更は一切不要になるでしょう。こうしたスタイルのアーキテクチャと実装の本当の強みはそこにあります。必要に応じてライブラリクラス(FieldEntityEntityCollection)の機能を強化すれば、その結果を新旧双方のアプリケーションで直ちに利用できるのです。

補足

 本シリーズ記事の内容は、Dynamic Concepts Development Corp.が公開している「スマートアーキテクチャ」のガイダンスに基づいています。このアーキテクチャはオープンで、開発者コミュニティが制約なしに利用できる一連の推奨インターフェイスを備えています。商用ベンダは、このアーキテクチャを利用した実装やツールを自由に開発することができます。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

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

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

TheCPUWizard(TheCPUWizard)

 高性能かつ高信頼性のソフトウェアシステム開発に30年の経験を持つシニアソフトウェアアーキテクト。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3688 2009/04/08 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング