Я пытаюсь придумать, как сделать маленькое приложение более элегантным и сделать его более устойчивым к изменениям.
По сути, это своего рода калькулятор цен проекта, и проблема в том, что существует множество параметров, которые могут влиять на ценообразование. Я стараюсь не загромождать код большим количеством выражений if для каждого параметра, но все же у меня есть, например, операторы if в двух местах проверяют значение параметра size.
У меня есть книга Head First Design Patterns, и я попытался найти там идеи, но ближе всего я получил образец декоратора, в котором есть пример, в котором starbuzz устанавливает цены на кофе сначала в зависимости от добавленных приправ, а затем в упражнение, добавив параметр размера (Tall, Grande, Venti). Но это, похоже, не помогло, потому что добавление этого параметра все же добавляло сложность условия if во многих местах (и это упражнение, которое они не объяснили далее).
Чего я пытаюсь избежать, так это необходимости менять несколько классов в случае изменения параметра или добавления нового параметра, или, по крайней мере, изменения в как можно меньшем количестве мест (для этого есть какое-то причудливое слово принципа проектирования, которое я не т запоминаю: -)).
Здесь ниже код. В основном он рассчитывает цену для проекта, который имеет задачи «Написание» и «Анализ» с параметром размера и различными моделями ценообразования. В дальнейшем будут и другие параметры, такие как «Насколько новый продукт?» (Новый, 1-5 лет, 6-10 лет) и т. Д. Любой совет о лучшем дизайне будет высоко оценен, будь то «шаблон дизайна» или просто хорошие объектно-ориентированные принципы, которые сделают его устойчивым к изменениям (например, добавление другого размера или изменение одного из значений размера, которые нужно изменить только в одном месте, а не в нескольких предложениях if):
public class Project
{
private readonly int _numberOfProducts;
protected Size _size;
public Task Analysis { get; set; }
public Task Writing { get; set; }
public Project(int numberOfProducts)
{
_numberOfProducts = numberOfProducts;
_size = GetSize();
Analysis = new AnalysisTask(numberOfProducts, _size);
Writing = new WritingTask(numberOfProducts, _size);
}
private Size GetSize()
{
if (_numberOfProducts <= 2)
return Size.small;
if (_numberOfProducts <= 8)
return Size.medium;
return Size.large;
}
public double GetPrice()
{
return Analysis.GetPrice() + Writing.GetPrice();
}
}
public abstract class Task
{
protected readonly int _numberOfProducts;
protected Size _size;
protected double _pricePerHour;
protected Dictionary<Size, int> _hours;
public abstract int TotalHours { get; }
public double Price { get; set; }
protected Task(int numberOfProducts, Size size)
{
_numberOfProducts = numberOfProducts;
_size = size;
}
public double GetPrice()
{
return _pricePerHour * TotalHours;
}
}
public class AnalysisTask : Task
{
public AnalysisTask(int numberOfProducts, Size size)
: base(numberOfProducts, size)
{
_pricePerHour = 850;
_hours = new Dictionary<Size, int>() { { Size.small, 56 }, { Size.medium, 104 }, { Size.large, 200 } };
}
public override int TotalHours
{
get { return _hours[_size]; }
}
}
public class WritingTask : Task
{
public WritingTask(int numberOfProducts, Size size)
: base(numberOfProducts, size)
{
_pricePerHour = 650;
_hours = new Dictionary<Size, int>() { { Size.small, 125 }, { Size.medium, 100 }, { Size.large, 60 } };
}
public override int TotalHours
{
get
{
if (_size == Size.small)
return _hours[_size] * _numberOfProducts;
if (_size == Size.medium)
return (_hours[Size.small] * 2) + (_hours[Size.medium] * (_numberOfProducts - 2));
return (_hours[Size.small] * 2) + (_hours[Size.medium] * (8 - 2)) + (_hours[Size.large] * (_numberOfProducts - 8));
}
}
}
public enum Size
{
small, medium, large
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<int> quantities = new List<int>();
for (int i = 0; i < 100; i++)
{
quantities.Add(i);
}
comboBoxNumberOfProducts.DataSource = quantities;
}
private void comboBoxNumberOfProducts_SelectedIndexChanged(object sender, EventArgs e)
{
Project project = new Project((int)comboBoxNumberOfProducts.SelectedItem);
labelPrice.Text = project.GetPrice().ToString();
labelWriterHours.Text = project.Writing.TotalHours.ToString();
labelAnalysisHours.Text = project.Analysis.TotalHours.ToString();
}
}
В конце - простой текущий код вызова в событии изменения для выпадающего списка, который устанавливает размер ... (Кстати, мне не нравится тот факт, что мне нужно использовать несколько точек, чтобы добраться до TotalHours в конце и здесь, насколько я могу вспомнить, это нарушает «принцип наименьшего знания» или «закон деметры», так что вклад в это также будет оценен, но это не главный вопрос)
С уважением,
Андерс