I need help with the design of an application that I am am writing. In the application employees can book their work within a project (the so called booking object). The booking objects optionally can have budgets. When the budget of a booking object is exhausted then employees must not be able to book on that booking object.
The project leader should set the budget's amount in one of two ways:
- setting a decimal number which represents some monetary amount
- setting the amount through man-days
For example it is legal to say that the booking object X has a budget of 10,000$. It also legal to say that the budget of X consists of 2.5 man-days for a senior, 3 man-days for a junior, and 5.5 man-days for a support. Internally the man-day amount is calculated to a monetary amount (2.5 times price of a senior + 3 times price of junior + 5.5 times price of support). The man-day amount is somehow a calculator but it should be persisted, so not an UI thing only.
The question is how to inject the price(s) into the man-day budget without the man-day budget knowing too much of the booking object. Each booking object is allowed to have differernt prices for the levels ("senior", "junior", "support", etc.).
I came up with following classes.
// could be designed as an interface, too
public abstract class Budget
{
public abstract decimal Amount { get; }
}
Two special classes for the monetary amount and man-day amount.
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));
}
}
The consumer of the classes can now write something like this:
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
For cenvenience I have let out the implementation of the Budget.Amount property in the concrete classes and also the definition of ManDay.
There will be requirement that other objcets in the application will have a budget, too. It's not crystal clear yet. How should I design my classes so that ManDayBudget does not know too much about the price finding logic, better does not know anything about booking objects. I have the feeling that I am missing a class which does the calcualtion.
EDIT:
To be clear what the consumer should and can do:
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" }
});
The monetary amount of the booking object X (bo_x.Budget.Amount) can be 7.600 and the one of Y can be 9.200 because for each booking object different prices are defined. The consumer says this much man-day budget and nothing more. But the monetary amount must be calculated somehow without too much knowledge about BookingObject in order to reuse the class later.
EDIT 2:
X could have a price set for senior to 100, for junior to 80, etc. and Y could have set price for senior to 125, for junior to 100, etc. Therefore even the man-day amount is set to the same amount of days the monatary amount of the two booking objects differ.
I think you need to look at the Decorator design pattern. I will try to come up with a code example the represents your code; for now, here some links to help get you started:
http://www.codeproject.com/Articles/42042/Decorator-Design-Pattern http://www.dofactory.com/Patterns/PatternDecorator.aspx
EDIT:
Your decorator could look like this:
You pass into its ctor an object that does the price calculation. This is how the budgets themselves will never need to know how the price is calculated, another object takes care of that. Then you use the decorator like so:
I've changed the decorator slightly. also if you change the ManDayBudget a bit:
EDIT:
Forgot to mention, that the decorator example if a geernic one, in that it does not care about the type of
Budgetbeing passed into it. Another way to achieve what you want is to change the constructor of the ManDayBudget class to accept aICalculatorobject and use it inside the ManDayBudget class, rather then using a decorator. It just depends on how generic you wanted the code to be like.