我正在努力找出 Memento 模式的问题。虽然我理解它并且我能够实现它,但我必须遗漏一些东西,因为在我看来,如果应用于具有List
类型属性的对象时会失败。
考虑以下类:
public class LEDTV
{
public string Size { get; set; }
public List<Input> Inputs { get; set; }
public Manufacturer Manufacturer { get; set; }
public LEDTV(string size, List<Input> inputs, Manufacturer manufacturer)
{
Size = size;
Inputs = inputs;
Manufacturer = manufacturer;
}
public void AddInput(Input input)
{
Inputs.Add(input);
}
public string GetDetails()
{
string inputs = string.Join(";", Inputs);
return "LEDTV [Size=" + Size + ", Inputs=[" + inputs + "], Manufacturer=" + Manufacturer + "]";
}
public Memento SaveState()
{
return new Memento(this);
}
public void RestoreState(Memento memento)
{
Size = memento.Size;
Inputs = memento.Inputs;
Manufacturer = memento.Manufacturer;
}
}
public class Memento
{
public string Size { get; set; }
public List<Input> Inputs { get; set; }
public Manufacturer Manufacturer { get; set; }
public Memento(LEDTV ledTV)
{
Size = ledTV.Size;
Inputs = ledTV.Inputs;
Manufacturer = ledTV.Manufacturer;
}
public string GetDetails()
{
return "Memento [Size=" + Size + ", Inputs=[" + Inputs + "], Manufacturer=" + Manufacturer + "]";
}
}
现在考虑以下代码:
LEDTV ledTV;
Stack<Memento> mementos = new Stack<Memento>();
// Led TV #1
ledTV = new LEDTV("42 inch", new List<Input>() { new Input("VGA", "") }, new Manufacturer("Samsung"));
mementos.Push(new Memento(ledTV));
Console.WriteLine("\nCurrent LedTV : " + ledTV.GetDetails());
// Make changes to Led TV #1
ledTV.Size = "36 inch";
ledTV.Manufacturer = new Manufacturer("Sony");
ledTV.AddInput(new Input("Digital Audio", ""));
// Led TV #2
ledTV = new LEDTV("46 inch", new List<Input>() { new Input("SCART", "") }, new Manufacturer("LG"));
mementos.Push(new Memento(ledTV));
Console.WriteLine("\nCurrent LedTV : " + ledTV.GetDetails());
// Ledt TV #3
ledTV = new LEDTV("50 inch", new List<Input>() { new Input("HDMI", "") }, new Manufacturer("Toshiba"));
Console.WriteLine("\nCurrent LedTV : " + ledTV.GetDetails());
while (mementos.Any())
{
Console.WriteLine("\nRestoring to previous LED TV");
ledTV.RestoreState(mementos.Pop());
Console.WriteLine("\nCurrent LedTV : " + ledTV.GetDetails());
}
所以,简而言之,我有一个 LEDTV 类型,它有一个用于大小的 ValueType、一个可用输入列表和一个用于制造商的非 ValueType。Memento 对象接受 LEDTV 来保存其状态。代码片段设置 LEDTV,保存其状态,然后对其进行一些更改,再次设置并保存其状态,再次设置,最后回滚所有保存的状态,产生以下输出:
Current LedTV : LEDTV [Size=42 inch, Inputs=[VGA], Manufacturer=Samsung]
Current LedTV : LEDTV [Size=46 inch, Inputs=[SCART], Manufacturer=LG]
Current LedTV : LEDTV [Size=50 inch, Inputs=[HDMI], Manufacturer=Toshiba]
Restoring to previous LED TV
Current LedTV : LEDTV [Size=46 inch, Inputs=[SCART], Manufacturer=LG]
Restoring to previous LED TV
Current LedTV : LEDTV [Size=42 inch, Inputs=[VGA;Digital Audio], Manufacturer=Samsung]
一切都按预期工作,除了 List 属性,我显然可以理解为什么:列表不是 ValueType,所以它是通过引用传递的。对其所做的任何更改都会反映在 LEDTV 和 Memento 中,因为这两个对象中的列表相同。当然,我可以将列表内容复制到另一个列表中,从而创建一个新的列表对象,但保留构成列表的对象的所有引用,但这看起来很hacky。此外,对我来说,基本上拥有一个我想要跟踪状态的所有类的重复类似乎是不可行的,因为这基本上就是纪念品。一个理想的解决方案是一个通用的 Memento 实现,它列出一个类型的属性并保存每个属性,但是我必须通用地克服 List 问题,这乍一看似乎是不可能的。
任何帮助都将不胜感激,无论是以具体解决方案的形式,还是只是指出方式,或者只是告诉我我误解了整个概念。