2009-04-28 1 views
6

私はアドバイスを求めたり、「あなたは何と思いますか?私は考えていますか?これはです。読んで、あなたの考えを知らせてください。受注クラスに料金表や割引を組み込むか、商品ラインにする必要がありますか

説明よりも表示が簡単ですが、アプリは本質的に3つの主要な部分(商品、OrderItems、Order)を持つPOSアプリと似ています。

アイテムクラスは、データストアからのデータです。

public class Item 
    : IComparable<OrderItem>, IEquatable<OrderItem> 
{ 
    public Int32 ID { get; set; } 
    public String Description { get; set; } 
    public decimal Cost { get; set; } 

    public Item(Int32 id, String description, decimal cost) 
    { 
     ID = id; 
     Description = description; 
     Cost = cost; 
    } 
    // Extraneous Detail Omitted 
} 

オーダーアイテムクラスはオーダーのアイテムラインです。あなたは順番に手数料や割引を追加するとき

public class OrderItem 
    : Item, IBillableItem, IComparable<OrderItem>, IEquatable<OrderItem> 
{ 
    // IBillableItem members 
    public Boolean IsTaxed { get; set; } 
    public decimal ExtendedCost { get { return Cost * Quantity; } } 

    public Int32 Quantity { get; set; } 

    public OrderItem (Item i, Int32 quantity) 
     : base(i.ID, i.Description, i.Cost) 
    { 
     Quantity = quantity; 

     IsTaxed = false; 
    } 
    // Extraneous Detail Omitted 
} 

は現在、それは同じくらい簡単です:私はそれが好き

Order order = new Order(); 
// Fee 
order.Add(new OrderItem(new Item("Admin Fee", 20), 1)); 

// Discount 
order.Add(new OrderItem(new Item("Today's Special", -5), 1)); 

、それは感覚とOrderは内の項目を反復処理を継承する基本クラスを作ります適切な税金を計算し、他のOrder-type文書(2がある)が、何も補完せずにこれをすべて計算する基本クラスから継承できるようにします。オーダー・タイプの文書に割引がない場合は、$ value OrderItemを追加しないだけの簡単な方法です。

唯一の問題は、このデータを表示することです。このフォームには、販売アイテム(つまり、手数料/割引ではない)を表示するグリッドがあります。同様に、特定の料金と特定の割引のテキストボックスがあります。私はこのクラスのフィールドにそれらのUI要素をデータバインドしたいので、ユーザー(と私)にとっては簡単です。 IHasFees、IHasDiscountsをとOrderはそれらを実装する必要があり;:

MY THOUGHT

2つのインターフェイスを持っていますどちらもリストの単一メンバーを持ちます。そうすれば、私はセールアイテムだけにアクセスすることができ、料金と唯一の割引(必要ならばコントロールにバインド)することができます。

私はそれについて好きではない: - 今、私は3種類のアドオンは、/クラス(AddItemメソッド/ AddFee/AddDiscount /削除...) ための方法を取り除く持っている - 私はtriplicating(複製てる? )機能はすべて同じタイプのアイテムのリストであり、各リストは異なる意味を持つからです。

私は正しい経路にいますか?私はこれが大部分の人にとって解決された問題であると考えています(この種のソフトウェアは非常に一般的です)。

public IList<OrderItem> Fees 
{ 
    get 
    { 
     return _items.Find(i=>i.ItemType==ItemType.Fee); 
    } 
} 

を今、あなたはまだあなたの単一のリストを維持し、余分なインターフェースを避けることができます。

+7

IHasFeesとIHasDiscounts音不器用にLOLCatsが好きです。 –

答えて

3

私は(私はALT.netの提唱者ではないんだけど、推論が健全に見えた)私はずっと前に聞いALT.netポッドキャストにロブ・コネリーによって発言にあなたを指します:

を何「ビジネスユーザー」(あなたの周りの人がいる場合)に意味があります。

プログラマーとして、類似の属性と振る舞いを持つため、Item、Fee、Discountなどを考慮する必要があります。

しかし、それらはモデルの点で2つの全く別個の概念であるかもしれない。そして、誰かが後で来るだろう、と言っている "これは意味がない、彼らは別々のものです、私はそれらを個別に報告する必要があり、私はこの場合に割引にこの特定のルールを適用する必要があります。

DRYはモデルを限定することを意味するものではなく、継承などを介して動作を因数分解する際に注意してください。

この場合に使用された特定の例は、ショッピングカートのものでした。プログラマーの自然なアイデアは、コミットされていない状態で注文を使用することでした。彼らはまったく同じように見えるので、意味があります。 それ以外はそうではありません。彼らは2つの別個のコンセプトであり、デザインをあまり明確にしないので、クライアントには意味をなさない。

それはしかし慣行、味や意見の問題であるので、盲目的にウェブサイトに掲載アドバイス:)

そして、あなたの特定の問題に従わない、私が一緒に仕事システムはアイテム、手数料を、使用しています(アイテムのプロパティ)とそのオーダーのグローバル割引(オーダーではないが、それはPOSレシートだが、その場合はそれほど重要ではない)。

私は、これらのコンセプトの背後にあるアイテムはインベントリ対象の特定のインスタンスであり、在庫量に影響を与え、それらは列挙可能であり、定量可能であると考えます。

料金はありません。彼らはほとんどの属性を共有していません。

あなたのドメインはそれよりはるかに制限されているように思われるかもしれませんが、あなたはこれらの問題を念頭に置いておきたいかもしれません。

+0

+1:私は、あなたが注文するアイテムとして割引を見るのは難しいですが、私はストレッチすることができますが、ILineItemを定義することでモデル化できます。数量、料金は注文の明細です。私はまだ同じ種類のカバーの下であっても、Item&Discountから別々のオーダーでそれらを公開するでしょう。 – JoshBerke

+0

私はPOSで作業しているシステムでは、料金には数量(それが領収書に記載されているかどうか)がないため実際にはありません。 私は、計算目的のために、集団目的(合計/小計計算)のための共通インタフェース実装を、行動特性として話すことに同意することができます。私はそれらを同じタイプにしてはいないでしょう(ちょうど腸の感覚)。 また、全体のコンテキストを持たないときやビジネスアナリストにアクセスしないときに「正しい答え」を出すのは難しいです。 –

1

1つのオプションは、今、あなたはあなたのためのクラスを持っている可能性が

enum ItemType 
{ 
    Item, 
    Fee, 
    Discount 
} 

をOrderItemのためにitemTypeに属性を追加することです。 IList GetItems(ItemType type)のようなメソッドを持つことさえできます。

あなたの現在のデザインでは、%の割引は認められていません。今日は10%オフになります。これは要件ではないかもしれませんが、アプリケーションがこれを計算しなければならないようにするための1つの選択肢は、割引からアイテムを分離することです。

10個の商品を5%オフにすると、ディスカウントはさらにルールになる可能性があります。

+0

+1私はこのアイデアが好きで、特にディスカウントがルールになるというコンセプトが好きです(それを使っているセールスマンの1人がしばしばそれに似ています)。 –

+0

うん、それは規則としてはるかに複雑ですが、その複雑さにはあなたができることがたくさんあります。特に、再コンパイルせずに新しいルールを注入できるのであれば... – JoshBerke

3

効果的には、あなたのデザインを細かく見て、の動作がどこにあるのか把握しようと思います。があります。それらのビヘイビアの共通点を明確なインタフェースに抽出し、それがあなたのデザインに当てはまることを確認してください。

ウィッツ;料金には、それに関連する妥当性確認行動が関連付けられている場合があります。たとえば、20項目以上のオーダーに料金を追加するとします(この例では、私と一緒に実行します)。今度は、20番目のアイテムを追加すると、その料金をオーダーに追加することができますが、問題があります。あなたがあなたの注文からアイテムを取り除くときは、毎回あなたの注文からその料金を取り除く必要があるかどうか確認する必要がありますか?疑わしい;ここでの示唆は、料金/割引と関連した動作があり、本質的にそれらをまったく異なるクラスにするということです。

私はこのように見ています。料金と割引を「特別なもの」として分類し、料金と割引の両方が継承する「ISpecial」インターフェースを作成します。一般的な機能をISpecialインターフェイス(たとえば、 "Validate")に抽出します。次に注文書にISpecial(または何でも)インターフェースを実装させます。

このようにして、特定のFee.Validate()ビヘイビアとDiscount.Validateビヘイビアを定義し、多態性の魔法(m_specialCollection.validateのforeach)のおかげで正しく動作させることができます。このようにして、必要になる可能性がある他のもの(税金など)のために特別なインターフェースを簡単に拡張することができます。

2

私がここで直面している問題の中核は、OrderItemをサブクラスItemとして実装していることです。これが実際には必ずしも適切ではないことがわかりました。あなたが記述するものを考える

、ここで私はこれを実装してみたい方法は次のとおりです。

は、あなたがデータバインディングに公開するすべての単一値のデータ要素に対するパブリックプロパティを実装Orderクラスを作成します。注文番号、日付を、顧客、総手数料、合計割引など。の具体的なの料金/割引を単一の値として表示する必要があるようです。そうであれば、それらの公共のプロパティを実装します。

グリッドにバインドするすべてのデータ要素、およびアイテムを並べ替えるすべてのデータ要素にパブリックプロパティを実装する抽象的なOrderItemクラスを作成します。 (また、このIOrderItemインターフェースを作ることができ、それは本当にすべての注文項目に共通のメソッドがあるように予定されているかどうかによって異なります。)

は、特定の種類のOrderItemのサブクラス(またはIOrderItemを実装するクラス)を作成します順番に表示できる行項目:ProductOrderItemFeeOrderItemDiscountOrderItemなど

ProductItemの実装では、タイプItemのプロパティを実装する - それは次のようになりたい:

public class ProductItem : OrderItem 
{ 
    public Item Item { get; set; } 
    public string Description { get { return Item.Description; } } 
    public int Quantity { get; set; } 
    public decimal Amount { get { return Item.Price * Quantity; } } 
} 

IEnumerable<OrderItem>のプロパティをOrder内に実装して、すべての広告申込情報を保存します。例えば、OrderItemsを追加するためのAddItemメソッドを実装します:

public void AddItem(OrderItem item) 
{ 
    _Items.Add(item); // note that backing field is a List<OrderItem> 
} 

あなたはかなり簡単に呼び出すことができ:例えば、このリストから値を抽出するために必要なものを単一値フィールドの

Order o = new Order(); 
o.AddItem(new ProductOrderItem { Item = GetItem(1), Quantity = 2 }); 
o.AddItem(new FeeItem { Description = "Special Fee", Amount = 100 }); 
o.AddItem(new DiscountItem { DiscountAmount = .05 }); 

書き込みの実装を:

public decimal TotalFees 
{ 
    get 
    { 
     return (from OrderItem item in Items 
       where item is FeeItem 
       select item.Amount).Sum(); 
    } 
} 

後で戻って、必要に応じてこれらのプロパティを最適化することができます(計算を一度行った後に計算を保存するなど)。

AddItemProductItemを追加するように制限し、Orderの他の方法を使用して他の種類のアイテムを追加することもできます。注文のをあなたが注文項目のグリッド内の適切な場所での割引額を表示したい場合は、このアプローチを使用すると思い

public void SetDiscountAmount(decimal discountAmount) 
{ 
    DiscountOrderItem item = _Items 
     .Where(x => x is DiscountOrderItem) 
     .SingleOrDefault(); 
    if (item == null) 
    { 
     item = new DiscountOrderItem(); 
     _Items.Add(item); 
    } 
    item.DiscountAmount = discountAmount; 
} 

、だけでなく、望んでいた:たとえば、順序が一つだけの割引額を持つことができる場合割引額は単一の値になります。(それはあなたが、OrderDiscountAmountプロパティを作りたいのセッターでDiscountOrderItemを作成し、Order.DiscountAmountからそのAmountを取得DiscountOrderItem持っている可能性があることを論証だ。私は両方のアプローチは、彼らの長所と短所を持っていると思う。)

関連する問題