假设我们有一个对象代表一块硬件的配置。为了论证,温度控制器(TempController)。它包含一个属性,即设定点温度。
我需要将此配置保存到文件中以便在其他设备中使用。文件格式(FormatA)是一成不变的。我不希望TempController对象知道文件格式......它与该对象无关。所以我创建了另一个对象“FormatAExporter”,它将TempController转换为所需的输出。
一年后,我们制作了一个新的温度控制器,我们称之为“AdvancedTempController”,它不仅具有设定值,而且还具有速率控制,这意味着还有一个或两个属性。还发明了一种新的文件格式来存储这些属性......我们称之为FormatB。
两种文件格式都能够代表两种设备(如果缺少设置,则假设AdvancedTempController具有合理的默认值。)
所以这就是问题所在:如果不使用'isa'或其他“作弊”方式来确定我拥有的对象类型,FormatBExporter如何处理这两种情况?
我的第一直觉是在每个温度控制器中都有一个方法,可以为该类提供客户导出器,例如TempController.getExporter()和AdvancedTempController.getExporter()。这不能很好地支持多种文件格式。
唯一想到的另一种方法是在每个温度控制器中有一个方法,它返回一个属性列表及其值,然后格式化程序可以决定如何输出它们。它有用,但这看起来很复杂。
更新:进一步工作后,后一种方法效果不佳。如果你的所有类型都很简单,但是如果你的属性是对象,那么你最终只是将问题推到一个级别......你被迫返回一对String,Object值,导出器必须知道什么对象实际上是要利用它们。所以它只是把问题推到了另一个层面。
对于我如何保持这种灵活性有什么建议吗?
答案 0 :(得分:4)
你可以做的是让TempControllers负责使用通用归档器来保持自己。
class TempController
{
private Temperature _setPoint;
public Temperature SetPoint { get; set;}
public ImportFrom(Archive archive)
{
SetPoint = archive.Read("SetPoint");
}
public ExportTo(Archive archive)
{
archive.Write("SetPoint", SetPoint);
}
}
class AdvancedTempController
{
private Temperature _setPoint;
private Rate _rateControl;
public Temperature SetPoint { get; set;}
public Rate RateControl { get; set;}
public ImportFrom(Archive archive)
{
SetPoint = archive.Read("SetPoint");
RateControl = archive.ReadWithDefault("RateControl", Rate.Zero);
}
public ExportTo(Archive archive)
{
archive.Write("SetPoint", SetPoint);
archive.Write("RateControl", RateControl);
}
}
通过保持这种方式,控制器不关心实际值的存储方式,但仍然保持对象的内部封装良好。
现在您可以定义一个所有归档类都可以实现的抽象Archive类。
abstract class Archive
{
public abstract object Read(string key);
public abstract object ReadWithDefault(string key, object defaultValue);
public abstract void Write(string key);
}
FormatA归档程序可以单向执行,FormatB归档可以另外执行。
class FormatAArchive : Archive
{
public object Read(string key)
{
// read stuff
}
public object ReadWithDefault(string key, object defaultValue)
{
// if store contains key, read stuff
// else return default value
}
public void Write(string key)
{
// write stuff
}
}
class FormatBArchive : Archive
{
public object Read(string key)
{
// read stuff
}
public object ReadWithDefault(string key, object defaultValue)
{
// if store contains key, read stuff
// else return default value
}
public void Write(string key)
{
// write stuff
}
}
您可以添加另一种Controller类型并将其传递给任何格式化程序。您还可以创建另一个格式化程序并将其传递给任何一个控制器。
答案 1 :(得分:1)
在C#或支持此功能的其他语言中,您可以执行此操作:
class TempController {
int SetPoint;
}
class AdvancedTempController : TempController {
int Rate;
}
class FormatAExporter {
void Export(TempController tc) {
Write(tc.SetPoint);
}
}
class FormatBExporter {
void Export(TempController tc) {
if (tc is AdvancedTempController) {
Write((tc as AdvancedTempController).Rate);
}
Write(tc.SetPoint);
}
}
答案 2 :(得分:0)
我有“temp controller”,通过getState方法,返回一个map(例如在Python中为dict,在Javascript中为对象,在C ++中为std :: map或std :: hashmap等)它的属性和当前值 - 它有什么问题?!它几乎不可能更简单,它完全可扩展,并且与它的使用完全分离(显示,序列化和& c)。
答案 3 :(得分:0)
如果FormatBExporter采用AdvancedTempController,那么您可以创建一个使TempController符合AdvancedTempController的桥接类。您可能需要向AdvancedTempController添加某种getFormat()函数。
例如:
FormatBExporter exporterB;
TempController tempController;
AdvancedTempController bridged = TempToAdvancedTempBridge(tempController);
exporterB.export(bridged);
还可以选择使用键到值映射方案。 FormatAExporter导出/导入键“setpoint”的值。 FormatBExporter导出/导入键“setpoint”和“ratecontrol”的值。这样,旧的FormatAExporter仍然可以读取新的文件格式(它只是忽略“ratecontrol”)而FormatBExporter可以读取旧的文件格式(如果缺少“ratecontrol”,它使用默认值)。
答案 4 :(得分:0)
嗯,很多都取决于你所谈论的文件格式。
如果它们基于键/值组合(包括嵌套的那些,如xml),那么拥有一种松散类型的中间内存对象可以在相应的文件格式编写器中抛出,这是一种很好的方法。
如果没有,那么你有一个场景,你有四种对象和文件格式的组合,每个场景都有自定义逻辑。在这种情况下,可能无法为每个可以处理任一控制器的文件格式提供单个表示。换句话说,如果你不能概括文件格式编写器,你就无法概括它。
我真的不喜欢控制器有出口商的想法 - 我只是不知道存储机制和其他什么的对象的粉丝(他们可能知道存储的概念,并给他们一个特定的实例通过一些DI机制)。但我认为你同意这一点,原因几乎相同。
答案 5 :(得分:0)
在OO模型中,作为集合的对象方法是控制器。如果您使用OO进行编程,将程序分成M和V更有用,而不是C。
答案 6 :(得分:0)
我想这是Factory method pattern适用的地方