代码之家  ›  专栏  ›  技术社区  ›  Chris Arguin

面向对象的模型与表示分离方法

oop
  •  6
  • Chris Arguin  · 技术社区  · 15 年前

    假设我们有一个表示硬件配置的对象。为了争论,一个温度控制器(温度控制器)。它包含一个属性,即设定点温度。

    我需要将这个配置保存到一个文件中,以便在其他设备中使用。文件格式(formata)设置为stone。我不想让tempcontroller对象知道文件格式…只是和那个物体无关。所以我创建了另一个对象“formataexporter”,将tempcontroller转换为所需的输出。

    一年后,我们制造了一个新的温度控制器,我们称之为“高级温度控制器”,它不仅有一个设定点,而且有速率控制,这意味着还有一个或两个属性。还发明了一种新的文件格式来存储这些属性…我们称之为formatb。

    两种文件格式都能够表示两种设备(如果缺少设置,则假定advancedtempcontroller具有合理的默认值)。

    所以这里有一个问题:如果不使用“isa”或其他“欺骗”的方法来确定我拥有的对象类型,那么formatBexporter如何处理这两种情况呢?

    我的第一直觉是在每个温度控制器中都有一个方法,可以为该类提供一个客户导出器,例如tempcontroller.getExporter()和advancedtempcontroller.getExporter()。这不支持多种文件格式。

    唯一让人想到的另一种方法是在每个温度控制器中都有一个方法,该方法返回属性及其值的列表,然后格式化程序可以决定如何输出这些属性。这是可行的,但这似乎很复杂。

    更新: 在进一步的工作中,后一种方法实际上并不奏效。如果您的所有类型都很简单,那么它可能很简单,但是如果您的属性是对象,那么您最终只会将问题推到一个级别上……您必须返回一对字符串、对象值,并且导出器必须知道对象实际要使用它们是什么。所以它只是把问题推向了另一个层次。

    对于如何保持这种灵活性有什么建议吗?

    7 回复  |  直到 15 年前
        1
  •  4
  •   jop    15 年前

    您可以做的是让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);
        }
    }
    

    通过保持这种方式,控制器不关心实际值是如何存储的,但您仍然保持对象内部的良好封装。

    现在,您可以定义一个所有归档类都可以实现的抽象归档类。

    abstract class Archive
    {
        public abstract object Read(string key);
        public abstract object ReadWithDefault(string key, object defaultValue);
        public abstract void Write(string key);
    }
    

    格式化存档程序可以用一种方法完成,而格式化存档程序可以用另一种方法完成。

    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
        }
    }
    

    您可以添加另一个控制器类型并将其传递给任何格式化程序。您还可以创建另一个格式化程序并将其传递给任何控制器。

        2
  •  1
  •   Ruud    15 年前

    在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);
        }
    }
    
        3
  •  0
  •   Alex Martelli    15 年前

    我会有一个“临时控制器”,通过一个GETStATE方法,返回一个映射(例如在Python中的一个DICT,在JavaScript中的一个对象,C++中的一个STD::MAP或STD::HASMAP等),它的属性和当前值——什么是卷绕的?!很难简单,它是完全可扩展的,并且完全脱离了它的用途(显示、序列化和C)。

        4
  •  0
  •   kyoryu    15 年前

    嗯,这很大程度上取决于你所说的文件格式。

    如果它们基于键/值组合(包括嵌套的键/值组合,如XML),那么拥有某种松散类型的中间内存对象,可以将其抛出到适当的文件格式编写器中,这是一种很好的方法。

    如果没有,那么您将得到一个场景,其中有四种对象和文件格式的组合,每个场景都有自定义逻辑。在这种情况下,可能无法为每个可以处理任一控制器的文件格式使用单个表示。换句话说,如果不能概括文件格式编写器,就不能概括它。

    我不太喜欢控制器有导出器的想法——我只是不喜欢了解存储机制和其他内容的对象(他们可能知道存储的概念,并通过一些DI机制向他们提供了一个特定的实例)。但我认为你同意这一点,原因基本相同。

        5
  •  0
  •   Tom Dalling    15 年前

    如果FormatBexporter采用AdvancedTempController,则可以创建一个使TempController符合AdvancedTempController的桥接类。不过,您可能需要向AdvancedTempController添加某种getFormat()函数。

    例如:

    FormatBExporter exporterB;
    TempController tempController;
    AdvancedTempController bridged = TempToAdvancedTempBridge(tempController);
    
    exporterB.export(bridged);
    

    还有使用键到值映射方案的选项。FORMATAExporter导出/导入键“设定点”的值。FORMTBExporter导出/导入键“Setpoint”和“RateControl”的值。这样,旧的formataexporter仍然可以读取新的文件格式(它只是忽略了“ratecontrol”),而formatbexporter可以读取旧的文件格式(如果缺少“ratecontrol”,则使用默认值)。

        6
  •  0
  •   clemahieu    15 年前

    在OO模型中,对象方法作为一个集合是控制器。如果使用OO编程,将程序分为M和V更有用,而不是C。

        7
  •  0
  •   LenW    15 年前

    我想这就是 Factory method pattern 将适用