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.