我有一个问题,我之前尝试过帮助,但我当时无法解决问题,所以我现在试图简化问题,看看能不能得到更具体的帮助这是因为它让我发疯了......
基本上,我有一个工作(更复杂)的应用程序版本,这是一个项目成本计算器。但是因为我在尝试学习更好地设计我的应用程序的同时,我想要一些关于如何改进这个设计的意见。基本上我想要的主要是输入条件(这里)在两个地方重复出现。我之前得到的建议是使用策略模式或工厂模式。我也知道Martin Fowler的书,其中建议使用多态来重构条件。我在他更简单的例子中理解这个原则。但是我怎么能在这里做这些事情(如果有的话)?我看待它的方式,计算取决于几个条件:1。它是什么类型的服务,写作或分析? 2.项目是小型,中型还是大型? (请注意,可能还有其他参数,同样不同,例如“产品是新产品还是以前存在的产品?”所以这些参数应该可以添加,但我试图保持示例简单只有两个参数是能得到具体的帮助)
因此,使用多态进行重构意味着创建了许多子类,我已经为第一个条件(服务类型)创建了这些子类,我是否应该为第二个条件创建更多的子类(大小)?会变成什么,AnalysisSmall,AnalysisMedium,AnalysisLarge,WritingSmall等...... ???不,我知道这不好,我只是不知道如何使用这种模式呢?
我看到同样的问题基本上是针对使用策略模式的建议(以及工厂模式,因为我认为它只是帮助实现上面的多态)。所以,如果有人有关于如何设计这些课程的具体建议,那么我将非常感激!还请考虑我是否也正确选择了对象,或者是否需要重新设计。 (像“你应该考虑工厂模式”这样的回应显然不会有所帮助......我已经走了这条路,我在这种情况下难以理解)
此致
的Anders
代码(非常简化,不介意我使用字符串而不是枚举,不使用配置文件进行数据等,这将在实际应用程序中根据需要完成这些设计问题):
public abstract class Service
{
protected Dictionary<string, int> _hours;
protected const int SMALL = 2;
protected const int MEDIUM = 8;
public int NumberOfProducts { get; set; }
public abstract int GetHours();
}
public class Writing : Service
{
public Writing(int numberOfProducts)
{
NumberOfProducts = numberOfProducts;
_hours = new Dictionary<string, int> { { "small", 125 }, { "medium", 100 }, { "large", 60 } };
}
public override int GetHours()
{
if (NumberOfProducts <= SMALL)
return _hours["small"] * NumberOfProducts;
if (NumberOfProducts <= MEDIUM)
return (_hours["small"] * SMALL) + (_hours["medium"] * (NumberOfProducts - SMALL));
return (_hours["small"] * SMALL) + (_hours["medium"] * (MEDIUM - SMALL))
+ (_hours["large"] * (NumberOfProducts - MEDIUM));
}
}
public class Analysis : Service
{
public Analysis(int numberOfProducts)
{
NumberOfProducts = numberOfProducts;
_hours = new Dictionary<string, int> { { "small", 56 }, { "medium", 104 }, { "large", 200 } };
}
public override int GetHours()
{
if (NumberOfProducts <= SMALL)
return _hours["small"];
if (NumberOfProducts <= MEDIUM)
return _hours["medium"];
return _hours["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)
{
Service writing = new Writing((int) comboBoxNumberOfProducts.SelectedItem);
Service analysis = new Analysis((int) comboBoxNumberOfProducts.SelectedItem);
labelWriterHours.Text = writing.GetHours().ToString();
labelAnalysisHours.Text = analysis.GetHours().ToString();
}
}
答案 0 :(得分:2)
在您的计算中,服务类型,服务规模和产品数量之间存在紧密联系,因此将它们分离为模块化块以应用策略模式非常困难。
如果计算系统是固定的,那么似乎策略模式不合适。如果不是......那么,为什么不简化系统?
例如,从服务大小中提取基本小时数,并根据您的其他设置应用各种折扣或增加。
public class Service
{
public IServiceSize serviceSize { internal get; set; }
public IServiceBulkRate serviceBulkRate { internal get; set; }
public IServiceType serviceType { internal get; set; }
public int numberOfProducts { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Service"/> class with default values
/// </summary>
public Service()
{
serviceSize = new SmallSize();
serviceBulkRate = new FlatBulkRate();
serviceType = new WritingService();
numberOfProducts = 1;
}
public decimal CalculateHours()
{
decimal hours = serviceSize.GetBaseHours();
hours = hours * serviceBulkRate.GetMultiplier(numberOfProducts);
hours = hours * serviceType.GetMultiplier();
return hours;
}
}
public interface IServiceSize
{
int GetBaseHours();
}
public class SmallSize : IServiceSize
{
public int GetBaseHours()
{
return 125;
}
}
public interface IServiceBulkRate
{
decimal GetMultiplier(int numberOfProducts);
}
public class FlatBulkRate : IServiceBulkRate
{
public decimal GetMultiplier(int numberOfProducts)
{
return numberOfProducts;
}
}
public class StaggeredBulkRate : IServiceBulkRate
{
public decimal GetMultiplier(int numberOfProducts)
{
if (numberOfProducts < 2)
return numberOfProducts;
else if (numberOfProducts >= 2 & numberOfProducts < 8)
return numberOfProducts * 0.85m;
else
return numberOfProducts * 0.8m;
}
}
public interface IServiceType
{
decimal GetMultiplier();
}
public class WritingService : IServiceType
{
public decimal GetMultiplier()
{
return 1.15m;
}
}
答案 1 :(得分:1)
我将移动逻辑以选择要计算到Service基类中的值,并将实际计算委托给每个子类:
public abstract class Service
{
private readonly int numberOfProducts;
private readonly IDictionary<string, int> hours;
protected const int SMALL = 2;
protected const int MEDIUM = 8;
public Service(int numberOfProducts, IDictionary<string, int> hours)
{
this.numberOfProducts = numberOfProducts;
this.hours = hours;
}
public int GetHours()
{
if(this.numberOfProducts <= SMALL)
return this.CalculateSmallHours(this.hours["small"], this.numberOfProducts);
else if(this.numberOfProducts <= MEDIUM)
return this.CalculateMediumHours(this.hours["medium"], this.numberOfProducts);
else
return this.CalculateLargeHours(this.hours["large"], this.numberOfProducts);
}
protected abstract int CalculateSmallHours(int hours, int numberOfProducts);
protected abstract int CalculateMediumHours(int hours, int numberOfProducts);
protected abstract int CalculateLargeHours(int hours, int numberOfProducts);
}
然后,如果任何计算特别复杂,您可以将其提取到策略对象中,并仅将其用于该特定子类。
编辑:如果您想支持任意数量的计算,您可以创建一个类来管理小时'类别'和每个类别的计算之间的映射。然后每个子类(或一些工厂)可以为每个类别提供相关的计算:
public class HoursCalculationStrategyCollection
{
private readonly Dictionary<string, int> hours;
private readonly Dictionary<string, Func<int, int, int>> strategies;
public HoursCalculationStrategyCollection(IDictionary<string, int> hours)
{
this.hours = hours;
this.strategies = new Dictionary<string, Func<int, int, int>();
}
public void AddCalculationStrategy(string hours, Func<int, int, int> strategy)
{
this.strategies[hours] = strategy;
}
public int CalculateHours(int numberOfProducts)
{
string hoursKey = null;
if(numberOfProducts <= SMALL)
hoursKey = small;
else if(...)
...
Func<int, int, int> strategy = this.strategies[hoursKey];
return strategy(this.hours[hoursKey], numberOfProducts);
}
}
答案 2 :(得分:0)
您可以将工厂和策略模式结合起来。然后,您的工厂将创建一个具体的服务,并通过一个策略来处理不同的大小(小,中或大)。
这将为您提供8个课程:服务,分析,写作,MediumStrategy,SmallStrategy,LargeStrategy和ServiceFactory +策略界面。
然后,ServiceFactory将包含用于决定使用哪种策略的代码。类似的东西:Analysis createAnalysis(int numberOfProducts) {
SizeStrategy strategy;
if (numberOfProducts <= SMALL) {
strategy = new SmallStrategy();
} else if (numberOfProducts <= MEDIUM) {
strategy = new MediumStrategy();
} else {
strategy = new LargeStrategy();
}
return new Analysis(numberOfProducts, strategy);
}
在这种情况下,您只能保存很少的代码。作为一项练习,这当然无关紧要,但我认为我不会浪费时间在实践中重构这一点。
编辑: 第二个想法,假设规则可能会改变,在我看来,control table可能比OOP模式更合适。