0

私が書いているアプリケーションの設計について助けが必要です。アプリケーションでは、従業員はプロジェクト内で作業を予約できます (いわゆる予約オブジェクト)。予約オブジェクトには、オプションで予算を設定できます。予約オブジェクトの予算が使い果たされた場合、従業員はその予約オブジェクトで予約できてはなりません。

プロジェクト リーダーは、次の 2 つの方法のいずれかで予算額を設定する必要があります。

  1. 金額を表す 10 進数の設定
  2. 工数で金額設定

たとえば、予約オブジェクト X の予算は 10,000$ であると言えます。X の予算は、シニア 2.5 人日、ジュニア 3 人日、サポート 5.5 人日で構成されていると言っても過言ではありません。内部的には、工数は金額として計算されます (シニアの価格の 2.5 倍 + ジュニアの価格の 3 倍 + サポートの価格の 5.5 倍)。工数は何らかの計算機ですが、永続化する必要があるため、UI だけではありません。

問題は、工数予算が予約オブジェクトをあまり知らずに、工数予算に価格を注入する方法です。各予約オブジェクトは、レベル (「シニア」、「ジュニア」、「サポート」など) に対して異なる価格を持つことができます。

私は次のクラスを思いつきました。

// could be designed as an interface, too
public abstract class Budget
{
  public abstract decimal Amount { get; }
}

金額と工数の 2 つの特別なクラス。

public class MonetaryBudget : Budget
{
  private decimal amount;
  public MonetaryBudget(decimal amount)
  {
    this.amount = amount;
  }
}

public class ManDayBudget : Budget
{
  private decimal amount;
  public ManDayBudget(IEnumerable<ManDay> manDays)
  {
    this.amount = manDays.Sum(md => md.ManDays * PriceOf(md.Level));
  }
}

クラスのコンシューマーは、次のように記述できるようになりました。

var bo_x = new BookingObject();
bo_x.Budget = new MonetaryBudget(10000);
bo_x.Budget = new ManDayBudget(new List<ManDay>
{
  new ManDay { ManDays = 2.5, Level = "senior" },
  new ManDay { ManDays = 3.0, Level = "junior" },
  new ManDay { ManDays = 5.5, Level = "support" }
});

var bo_y = new BookingObject();
bo_y.Budget = new ManDayBudget(new List<ManDay>
{
  new ManDay { ManDays = 2.5, Level = "senior" },
  new ManDay { ManDays = 3.0, Level = "junior" },
  new ManDay { ManDays = 5.5, Level = "support" }
});

// bo_x.Budget.Amount == bo_y.Budget.Amount is not guaranteed to evaluate to true

便宜上Budget.Amount、具象クラスでのプロパティの実装と の定義も公開しましたManDay

アプリケーション内の他のオブジェクトにも予算があるという要件があります。それはまだ明確ではありません。ManDayBudget価格検索ロジックについてあまり知らず、オブジェクトの予約について何も知らないほうがよいように、クラスをどのように設計すればよいでしょうか。計算を行うクラスが欠けているような気がします。

編集:

消費者がすべきこととできることを明確にするために:

var bo_x = new BookingObject();
bo_x.Budget = new ManDayBudget(new List<ManDay>
{
  new ManDay { ManDays = 2.5, Level = "senior" },
  new ManDay { ManDays = 3.0, Level = "junior" },
  new ManDay { ManDays = 5.5, Level = "support" }
});

var bo_y = new BookingObject();
bo_y.Budget = new ManDayBudget(new List<ManDay>
{
  new ManDay { ManDays = 2.5, Level = "senior" },
  new ManDay { ManDays = 3.0, Level = "junior" },
  new ManDay { ManDays = 5.5, Level = "support" }
});

予約オブジェクト X ( ) の金額はbo_x.Budget.Amount7.600 で、Y の金額は 9.200 です。これは、予約オブジェクトごとに異なる価格が定義されているためです。消費者は、これだけの工数予算を言いますが、それ以上は何も言いません。しかし、後でクラスを再利用するために、BookingObject についてあまり知識がなくても、何らかの方法で金額を計算する必要があります。

編集2:

X はシニアの価格を 100 に、ジュニアの価格を 80 に、などと設定することができ、Y はシニアの価格を 125 に、ジュニアの価格を 100 に設定することができます。日 2 つの予約オブジェクトの金額が異なります。

4

2 に答える 2

1

Decorator design patternを見る必要があると思います。あなたのコードを表すコード例を考えてみます。今のところ、開始するのに役立ついくつかのリンクを次に示します。

http://www.codeproject.com/Articles/42042/Decorator-Design-Pattern http://www.dofactory.com/Patterns/PatternDecorator.aspx

編集:

デコレータは次のようになります。

public class BudgetDecorator : Budget
{
    private readonly IPriceCalculator _calc;

    private Budget budget;

    public BudgetDecorator(Budget budget, IPriceCalculator calc)
    {
        _calc = calc;
        budget = budget;
    }

    public override decimal Amount
    {
        get
        {
            ManDayBudget dayBudget = budget as ManDayBudget;
            
            if (dayBudget != null)
            {
                return dayBudget.ManDays.Sum(md => md.ManDays * _calc.PriceOf(md.Level));
            }

            return budget.Amount;
        }
    }
}

価格計算を行うオブジェクトを ctor に渡します。これにより、予算自体が価格の計算方法を知る必要がなくなり、別のオブジェクトがそれを処理します。次に、デコレータを次のように使用します。

var b2 = new ManDayBudget(new List<ManDay>
{
    new ManDay { ManDays = 2.5, Level = "senior" },
    new ManDay { ManDays = 3.0, Level = "junior" },
    new ManDay { ManDays = 5.5, Level = "support" }
});

var d = new BudgetDecorator(b2, null /* Replace with object that does calcuation. */);

Console.WriteLine(d.Amount);

デコレータを少し変更しました。また、ManDayBudget を少し変更した場合:

public class ManDayBudget : Budget
{
    public IEnumerable<ManDay> ManDays { get; set; } // Add this property.

    public ManDayBudget(IEnumerable<ManDay> manDays)
    {
        this.ManDays = manDays;        
    }
    
    // Rest of code.....
}

編集:

言及するのを忘れていましたが、デコレータの例は、渡されるタイプを気にしないという点で、geernic の場合Budgetです。目的を達成するもう 1 つの方法は、オブジェクトを受け入れるように ManDayBudget クラスのコンストラクターを変更ICalculatorし、デコレーターを使用するのではなく、ManDayBudget クラス内で使用することです。それは、コードをどの程度一般的なものにしたいかによって異なります。

于 2013-08-15T10:32:56.417 に答える
0

あなたの質問は少し不明確です。

基本予算価格を注入するだけでよいことがわかった場合、解決策は少し簡単に思えます。ただし、これはデコレータ パターンと見なされることに注意してください。

public class ManDayBudget : Budget
{
  private decimal amount;
  public ManDayBudget(IEnumerable<ManDay> manDays, Budget baseBudget)
  {
    decimal baseAmount = baseBudget.Amount;
    this.amount = manDays.Sum(md => md.ManDays * PriceOf(md.Level));
    // do other things with baseAmount
  }
}

編集:

まだ不明ですが、要件の一部は把握できます。これが BookingObject の現在の設計であるとしましょう:

public class BookingObject{
    public decimal Price { get; set; }
    public Budget Budget{ get{ /*return */ }
        set{
            value.BookingObject = this;
            /*set to private*/
        }
    }
}

この設計では、Budget.Amountwill は明らかにその計算を に依存しており、それぞれの間に依存関係がありますBookingObject。さらに悪いことに、導出された予算ごとに異なる計算パラメータ (静的金額、工数など) を設定できるため、実装が難しくなります。PriceBudget.Amount

Budget クラスを再構築する

今のBudget授業は面倒です。Amount計算のロジックはありますがPrice、他のソースからの値が必要です。このように少し再設計Budgetできます。インターフェイスでも同じことができることに注意してください。

public abstract class Budget{
    public abstract decimal GetAmount(decimal price);
}

必要に応じて、入力パラメーターを別のコントラクトに再設計できIPriceます。BookingObject次に、次のように再設計できます。

public class BookingObject{
    public decimal Price { get; set; }
    public Budget Budget{ get; set; }
    public decimal Amount{
        get{
            return this.Budget.GetAmount(this.Price);
        }
    }
}

ManDayBudgetのサンプルですMonetaryBudget

public class ManDayBudget : Budget{
    public ManDayBudget(IEnumerable<ManDay> manDays)
    {
        this.manDays = manDays;
    }
    private readonly IEnumerable<ManDay> manDays;
    public override decimal Amount(decimal price){
        return price * // the calculation here
    }
}

public class MonetaryBudget : Budget{
    public MonetaryBudget(decimal amount)
    {
        this.amount = amount;
    }
    private readonly decimal amount;
    public override decimal Amount(decimal price){
        return amount;
    }
}

ただし、この設計には欠陥があります。まず、予算額を取得する方法が 2 つあります。

  1. decimal amount = bo.Amount
  2. decimal amount = bo.Budget.Amount(bo.Price);

第二に、BookingObjectは予算クラスに依存しています。Budget クラスにコンストラクターが挿入されている場合、予約オブジェクトのインスタンス化が難しくなります。

于 2013-08-15T16:48:28.700 に答える