Попытка избежать слишком большого знания о классе в другом классе
Мне нужна помощь в разработке приложения, которое я пишу. В приложении сотрудники могут забронировать свою работу в рамках проекта (так называемый объект бронирования). Объекты бронирования по желанию могут иметь бюджеты. Когда бюджет объекта бронирования исчерпан, сотрудники не должны иметь возможность бронировать этот объект бронирования.
Руководитель проекта должен установить сумму бюджета одним из двух способов:
- установка десятичного числа, которое представляет некоторую денежную сумму
- установка суммы в человеко-днях
Например, законно сказать, что бюджет объекта бронирования X составляет 10000$. Также законно сказать, что бюджет X состоит из 2,5 человеко-дней для старшего, 3 человеко-дней для младшего и 5,5 человеко-дней для поддержки. Внутренне количество человеко-дней рассчитывается в денежной форме (в 2,5 раза больше цены старшего + в 3 раза больше цены младшего + в 5,5 раза выше цены поддержки). Количество человеко-дней - это калькулятор, но его нужно сохранить, поэтому не только для пользовательского интерфейса.
Вопрос заключается в том, как ввести цену (и) в бюджет рабочего дня, при этом бюджет рабочего дня не будет знать слишком много объекта бронирования. Каждый объект бронирования может иметь разные цены для уровней ("старший", "младший", "поддержка" и т. Д.).
Я придумал следующие занятия.
// 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 и т. Д. Поэтому даже сумма в человеко-день установлена на то же количество дней различается общая сумма двух объектов бронирования.
2 ответа
Я думаю, вам нужно взглянуть на шаблон оформления Decorator. Я попытаюсь придумать пример кода, который представляет ваш код; сейчас вот несколько ссылок, которые помогут вам начать:
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, а не с помощью декоратора. Это зависит только от того, насколько вы хотите, чтобы код был похож.
Ваш вопрос немного неясен.
Если я понимаю, что вам нужно только ввести базовую бюджетную цену, похоже, решение может быть немного простым. Обратите внимание, это считается шаблоном декоратора.
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.Amount
очевидно, полагается на BookingObject
"s Price
для его 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;
}
}
Этот дизайн имеет недостатки, хотя. Во-первых, теперь есть 2 способа получения суммы бюджета.
decimal amount = bo.Amount
decimal amount = bo.Budget.Amount(bo.Price);
Во-вторых, BookingObject
имеет зависимость от бюджетного класса. Если в класс Budget добавлен конструктор, это усложнит создание экземпляра объекта бронирования.