我一直在经历Head First Design Patterns(最近刚刚进入),我正在阅读战略模式,我想到它可能是实施计算税收等常用方法的好方法。我在工作中使用的所有特定对象,但我有一个问题。
这就是我的想法:
public interface ITax
{
decimal ProvincialTaxRate { get; set; } // Yes, I'm Canadian :)
decimal CalculateTax(decimal subtotal);
}
public SaskatchewanTax
{
public decimal ProvincialTaxRate { get; set; }
public SaskatchewanTax()
{
ProvincialTaxRate = new decimal(0.05f);
}
public decimal CalculateTax(subtotal)
{
return ProvincialTaxRate * subtotal + FederalTaxRate * subtotal;
}
}
public OntarioTax
{
public decimal ProvincialTaxRate { get; set; }
public OntarioTax()
{
ProvincialTaxRate = new decimal(0.08f);
}
public decimal CalculateTax(decimal subtotal)
{
return ProvincialTaxRate * subtotal + FederalTaxRate * subtotal;
}
}
你可能已经注意到没有FederalTaxRate的声明,这就是我想问的问题。应该去哪儿?
所有税务计算器是否应该继承静态定义的其他类以及ITax?
public class TaxCalculator
{
public static decimal FederalTaxRate = new decimal(0.05f);
}
答案 0 :(得分:22)
我认为这是模式滥用的常见情况。
如果你检查两个“策略”,他们确实完全相同。唯一改变的是ProvincialTaxRate。
我会把事情保持干燥并且不要过度使用这种模式(或任何其他模式),这里你获得了一点灵活性,但是你也有2个类别没有拉动它们的重量,并且可能你不需要那种灵活性。
当您学习新技术或洞察力时,这种情况很常见,您希望在任何地方应用它(它发生在我们每个人身上),即使这样做会损害代码的可读性和可维护性。
我的意见:保持简单
此致
编辑(回应作者对我的回答的评论)
我没有试图取笑你或任何人。这是一个常见的错误,我做了很多次,并且很难学习它,不仅是模式,还有花哨的框架,服务器,新的流行语技术,你可以这么说。
这本书的作者自己警告读者不要过度使用模式,这个答案中的赞成也清楚地表明了一些东西。
但如果由于某种原因你仍然想要实现这种模式,那么这是我的拙见:
为这两个策略创建一个超类,这个超类将是抽象的,并且应该包含其子策略的共享速率值(FederalTaxRate)
在每个子类中继承并实现抽象方法“Calculate”(这里你会看到两种方法都是一样的,但让我们继续)
尝试让每个具体策略不变,总是赞成不变性,正如Joshua Bloch所说。为此,删除ProvincialTaxRate的setter并在其构造函数上指定值或直接在其声明中。
最后,我在StrategySuperclass中创建了一些静态工厂方法,以便您将客户端与实现或具体策略(现在很可能是受保护的类)分离开来。
编辑II: 这是一个带有一些(伪)代码的牧师,使解决方案更加清晰
希望有所帮助
此致
答案 1 :(得分:2)
在我看来,您有正确的解决方案 - 创建一个基类,其中包含所有派生类可以继承的加拿大联邦传真速率。静态地定义它是一个非常好的主意。您还可以使FederalTaxRate仅定义税率的访问者函数,以便您可以在运行时从文件或其他内容定义它。
我不认为这是唯一最好的解决方案,但它可以很好地工作。设计模式不应该妨碍你的常识,我认为常识会很好地解决这个问题。
答案 2 :(得分:2)
您可能希望从此代码开始,然后从那里开始:
public interface ITax
{
decimal CalculateTax(decimal subtotal);
}
public class SaskatchewanTax : ITax
{
private readonly decimal provincialTaxRate;
private readonly decimal federalTaxRate;
public SaskatchewanTax(decimal federalTaxRate)
{
provincialTaxRate = 0.05m;
this.federalTaxRate = federalTaxRate;
}
public decimal CalculateTax(decimal subtotal)
{
return provincialTaxRate * subtotal + federalTaxRate * subtotal;
}
}
public class OntarioTax : ITax
{
private readonly decimal provincialTaxRate;
private readonly decimal federalTaxRate;
public OntarioTax(decimal federalTaxRate)
{
provincialTaxRate = 0.08m;
this.federalTaxRate = federalTaxRate;
}
public decimal CalculateTax(decimal subtotal)
{
return provincialTaxRate * subtotal + federalTaxRate * subtotal;
}
}
此时,有两个不同的策略对象代表税收计算可能没什么意义,但实际上更实际(我假设税收计算更复杂,而且省份变化更多),这可能有意义。
但是,您应该考虑应用“可能最有效的方法”原则,并且只在您认为需要时才使用策略模式。
答案 3 :(得分:2)
为什么不忘记接口,只使用继承:
public abstract class Tax
{
protected decimal ProvincialTaxRate; // Yes, you are Canadian ;)
public decimal CalculateTax(decimal subtotal)
{
return ProvincialTaxRate * subtotal + FederalTaxRate * subtotal;
}
decimal FederalTaxRate = new decimal(0.20f);
}
public class SaskatchewanTax : Tax
{
public SaskatchewanTax()
{
base.ProvincialTaxRate = new decimal(0.05f);
}
}
public class OntarioTax : Tax
{
public OntarioTax()
{
base.ProvincialTaxRate = new decimal(0.08f);
}
}
如果您需要接口,只需在基类中实现它,只需将派生类用于自定义行为/行为。
答案 4 :(得分:1)
几点:
ProvincialTaxRate
几乎肯定在接口级别是不可变的(没有set
属性)。更改税率似乎不是一个好主意,但这意味着您无法在实施中使用自动属性。
如果只有一个FederalTaxRate
并且它只是一个简单的数值,我认为抽象基类是一种安全的方法。
不太可取:根据税收的工作方式,你可以说CalculateTax
取决于FederalTaxRate
因此需要作为参数提供(可能有不同的FederalTaxRates
并且你不希望CalculateTax
必须知道它们。)
不要让设计模式的定义妨碍好主意。他们是模式,而不是公式。 ;)
P.S。我是美国人,所以如果加拿大的税收实际上很简单,我希望国税局明年可以从你的书中拿出一页!
答案 5 :(得分:0)
只是深思熟虑 - 把这个方法放在适当的类中,然后调用它会有什么问题?
public decimal CalculateTax(decimal subtotal, decimal provincialTaxRate, decimal federalTaxRate) {
return provincialTaxRate * subtotal + federalTaxRate * subtotal;
}
据我所知,您希望为每个省使用不同的省级费率,但肯定不应该在接口实施中进行硬编码?
答案 6 :(得分:0)
已经给出了很多好的答案。只是为了加我的两分钱。使用这样的设计模式时:
如果您遇到需要州税的税收策略模式有10种情况,而政府税则需要5种策略模式,您可以制作两个派生抽象类(例如StateTax和GovernmentTax),源自主要摘要class(Tax?)和StateTax和GovernmentTax可以派生出具体的类(如OntarioTax,TexasTax等)。如果以后需要更改税收类型,您可以让它从另一个Tax类派生,同时保持所有通用代码不变。