0

我需要帮助设计我正在编写的应用程序。在应用程序中,员工可以在项目中预订他们的工作(所谓的预订对象)。预订对象可以有选择地具有预算。当预订对象的预算用尽时,员工不得在该预订对象上进行预订。

项目负责人应通过以下两种方式之一设置预算金额:

  1. 设置一个十进制数,代表一些货币金额
  2. 通过人工天数设置金额

例如,可以合法地说预订对象 X 的预算为 10,000 美元。同样合法的是,X 的预算包括高级人员 2.5 人日、初级人员 3 人日和支持人员 5.5 人日。在内部,人日金额计算为货币金额(高级价格的 2.5 倍 + 初级价格的 3 倍 + 支持价格的 5.5 倍)。man-day 金额在某种程度上是一个计算器,但它应该被持久化,所以不仅仅是 UI 的东西。

问题是如何在工日预算不了解预订对象太多的情况下将价格注入工日预算。每个预订对象的级别(“高级”、“初级”、“支持”等)允许有不同的价格。

我想出了以下课程。

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

货币金额和人日金额的两个特殊类别。

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.Amount可以是 7.600,Y 中的一个可以是 9.200,因为对于每个预订对象定义了不同的价格。消费者说这么多人工日预算,仅此而已。但是必须在没有太多关于 BookingObject 的知识的情况下以某种方式计算货币金额,以便以后重用该类。

编辑2:

X 可以将老年人的价格设置为 100、初级到 80 等,而 Y 可以将老年人的价格设置为 125、初级到 100 等。因此,即使人工天数也设置为相同的数量天两个预订对象的金额不同。

4

2 回答 2

1

我认为您需要查看装饰器设计模式。我将尝试提出一个代表您的代码的代码示例;现在,这里有一些链接可以帮助您入门:

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.....
}

编辑:

忘了提一下,如果装饰器示例是通用的,则它不关心Budget传递给它的类型。实现您想要的另一种方法是更改​​ 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'sPrice进行Budget.Amount计算,从而在每个之间建立依赖关系。更糟糕的是,每个派生的预算都可以有不同的计算参数(静态货币、工日等),这使得实施变得更加困难。

重建预算类

现在的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;
    }
}

这种设计虽然有缺陷。首先,现在有两种方法可以检索预算金额。

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

其次,BookingObject对预算类有依赖性。如果预算类是构造函数注入的,那么它将使预订对象的实例化变得更加困难。

于 2013-08-15T16:48:28.700 回答