所以我创建了一个Monopoly控制台应用程序来探索一些设计概念,并试图更好地理解测试驱动开发。
在这种情况下,我创建了单元测试,以确保我的所有类都可以正确地序列化和反序列化(因为我将来需要它,如果它们不能,我宁愿早点知道比以后所以我可以避免返工;事实上,这样做有助于我在初始设计中遇到一些缺陷。)
此属性是较大的Property类的一部分。 Owner
是拥有该财产的玩家。
我在这里想要实现的是,当玩家抵押特定房产时,该房产应标记为“抵押”,并且玩家应以现金收取购买价格的一半。当他们不归还财产时,他们会还钱。还有一些我还没有写过的其他检查;例如,游戏规则规定,如果房产仍有房屋,则不允许抵押房产。我还没有检查玩家是否有足够的钱来取消抵押财产,但我计划尽快添加,因为这是一个微不足道的规则来实施。这就是为什么我在这里使用setter而不是自动生成的属性。
这是问题所在:这序列化和反序列化而不抛出异常,但是如果我序列化抵押财产,它会在我反序列化时再次添加钱(即反序列化基本上具有重新抵押财产的效果) ,不用说单元测试失败了(应该给出明显的错误)。我认为这并不奇怪,因为这正是我写它的方式 - 当你改变它的价值时,它会抵押或不归还财产。
以下是代码:
[JsonProperty(Order = 1)]
public Player Owner
{
get;
set;
}
private bool _isMortaged = false;
[JsonProperty(Order = 2)]
public bool IsMortgaged
{
get { return _isMortaged; }
set
{
if (!IsOwned)
{
throw new InvalidOperationException("You can't mortgage a property that no one owns");
}
// If we're setting this property to the same thing that it
// already was, log it. You could argue that that would indicate
// a bug (it probably would), but throwing an exception here
// breaks deserialization.
Debug.WriteIf(value == _isMortaged, $"Setting IsMortgaged to the same thing as it was before. Stack trace:{Environment.NewLine}{(new StackTrace()).ToString()}");
// If we're setting this to the same thing that it already was,
// then the setter should have no effect at all.
if (value != _isMortaged)
{
// The player is mortgaging the property
if (value)
{
_isMortaged = true;
Owner.Money += (PurchasePrice / 2);
}
// The player is unmortgaging the property
else
{
_isMortaged = false;
Owner.Money -= (PurchasePrice / 2);
}
}
}
}
是否有一种简单的方法可以使用不同的setter进行反序列化,或者判断我是否在setter中进行反序列化? (后者似乎是“黑客”)。我想也有可能编写一个自定义反序列化器,这可以解决我想的问题,但我更愿意在可能的情况下避免这种情况(这似乎不是一种特别“干净”的方式)。< / p>
或者,这只是一个设计缺陷吗?
答案 0 :(得分:7)
这是一个设计缺陷; setters不应该有明显更新值的副作用(以及那些预期的那些,例如INotifyPropertyChanged
)
如果你需要特殊的行为,那么要么引入一种特殊的设置方法,要么反过来考虑它:
您抵押/取消抵押财产,因此您希望该财产余额的所有者更新
而不是让属性更新所有者的余额(它不应该关注:Separation of Concerns;而是可以动态计算所有者的余额属性:
public class Property {
public bool IsMortgaged {get;set;}
}
public class Player
{
private List<Property> _properties = new List<Property>();
private double _cash = 0;
public int AvailableMoney =>
_cash + _properties.Where(p => p.IsMortgaged).Select(p => p.MortgageValue).Sum();
}
现在,显然需要更新以正确匹配您的模型,但理想情况下,您希望数据尽可能愚蠢;财产是否抵押是一个简单的真/假。这是一种特殊的平衡,并且没有理由不重新计算它。
答案 1 :(得分:1)
这违反了单一责任或分离问题。
我将采用的方法是在class
PERSON
inherit
STRING
end
上设置一个私有的setter,并使用一些已存在的JSON.NET功能对其进行反序列化。
这需要Property.IsMortgaged
和Property
对于Owner
,您需要添加:
Property
并在public class Property
{
public double Value {get;} = 5000.00
private bool _isMortgaged
public bool SetMortgaged()
{
//Validation for if the property can be mortgaged goes here, return false if fails
if(_isMortgaged == true)
{
return false;
}
_isMortgaged = true;
return true;
}
}
类
Owner
这会将逻辑封装在各自的类中,public class Owner //or player
{
private double _cash;
public double AvailableCash
{
get {return _cash;}
}
public void MortgageProperty(Property item)
{
if(item.SetMortgaged())
{
_cash += item.Value / 2
}
else
{
//Throw an error or something else to let the user know it failed
}
}
}
会知道它是否可以抵押,而Property
会处理这笔钱。