|
任天堂が、大画面の「ニンテンドーDSi LL」を発表。欲しいと思いますか?
|
スマートなアプリケーションアーキテクチャの構築(1)はじめにこの連載では、ほぼすべてのサイズのプログラムで利用できる簡単なアプリケーションアーキテクチャの作成について見ていきます。記事中で使用するコードは概念を説明するためのものであり、実際に使用するためのものではありません。パート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; }
}
private decimal m_ExtendedPrice;
public decimal ExtendedPrice
{
get { return m_ExtendedPrice; }
set { m_ExtendedPrice = value; }
}
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>();
}
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年の経験を持つシニアソフトウェアアーキテクト。
関連記事 最新トップニュース
|
|