代码之家  ›  专栏  ›  技术社区  ›  Stein Åsmul

类设计:文件转换逻辑和类设计

  •  2
  • Stein Åsmul  · 技术社区  · 15 年前

    这是非常基本的,但有点像一个普通的问题,所以我想听听人们的想法。在这种情况下,我需要获取一个现有的msi文件,并用一些标准修改对其进行更新,然后输出一个新的msi文件(用更改复制旧文件)。

    我开始用一些公共方法和原始MSI的基本输入路径编写这个脚本。问题是,要使其正常工作,必须遵循调用方的严格调用路径:

    var custom = CustomPackage(sourcemsipath);
    custom.Duplicate(targetmsipath);
    custom.Upgrade();
    custom.Save();
    custom.WriteSmsXmlFile(targetxmlpath);
    

    将所有转换逻辑作为构造函数的一部分,而不是将它们作为公共方法提供,这样会更好吗?(为了避免让来电者知道“正确的顺序”是什么):

    var custom = CustomPackage(sourcemsipath, targetmsipath); // saves converted msi
    custom.WriteSmsXmlFile(targetxmlpath); // saves optional xml for sms
    

    然后,构造函数将直接复制msi文件,将其升级并保存到目标位置。“writesmsxmlfile仍然是一个公共方法,因为它并非总是必需的。

    就我个人而言,我不喜欢让构造函数实际“做一些事情”——我更喜欢能够调用公共方法,但是假设调用方应该知道正确的调用顺序似乎是错误的?

    另一种方法是先复制文件,然后将复制的文件传递给构造函数——但最好让类自己来完成这一操作。

    也许我把这些都倒过来了,需要两门课来代替: 源代码 , 目标包 并将sourcepackage传递到targetpackage的构造函数中?

    5 回复  |  直到 10 年前
        1
  •  1
  •   Stein Åsmul    10 年前

    可能只有我一个人,但一想到一个建设者要做所有这些事情,我就不寒而栗。但为什么不提供 静止的 方法,执行所有这些操作:

    public class CustomPackage 
    {
    
        private CustomPackage(String sourcePath) 
        {
             ...
        }
    
        public static CustomPackage Create(String sourcePath, String targetPath) 
        {
             var custom = CustomPackage(sourcePath);
             custom.Duplicate(targetPath);
             custom.Upgrade();
             custom.Save();
             return custom;
        }
    }
    

    这个方法的实际优点是,您不必给出 CustomPackage 除非转换过程实际成功(可选部件安全)。

    编辑 在C中,根据 Factory Pattern :

    public interface ICustomizedPackage 
    {
        ...
    }
    
    public class CustomPackage: ICustomizedPackage 
    {
        ...
    }
    
    public class Consumer 
    {
        public delegate ICustomizedPackage Factory(String,String);
    
        private Factory factory;
    
        public Consumer(Factory factory) 
        {
            this.factory = factory;
        }
    
        private ICustomizedPackage CreatePackage() 
        {
            return factory.Invoke(..., ...);
        }
    
        ...
    }
    

    后来:

    new Consumer(CustomPackage.Create);
    
        2
  •  2
  •   CPerkins    15 年前

    我同意你的第一个想法:把所有的转换逻辑放在一个地方。没有理由向用户公开该序列。

    顺便说一下,我同意您不将操作放入构造函数。我可能不会在构造函数中这样做,而是在单独的转换器方法中这样做,但这是我个人的爱好。

        3
  •  1
  •   Welbog    15 年前

    您认为构造函数不应该做更多的工作,而应该简单地初始化对象,这是正确的。

    我觉得你需要的是 Convert(targetmsipath) 包装调用的函数 Duplicate , Upgrade Save 从而消除了调用方知道正确操作顺序的需要,同时将逻辑从构造函数中保留出来。

    您还可以重载它以包括 targetxmlpath 当提供时,还调用 WriteSmsXmlFile 功能。这样,所有相关的操作都是从调用方一侧的同一个函数调用的,并且操作顺序总是正确的。

        4
  •  1
  •   Stein Åsmul    10 年前

    在这种情况下,我通常使用以下设计:

    var task = new Task(src, dst); // required params goes to constructor
    task.Progress = ProgressHandler; // optional params setup
    task.Run();
    
        5
  •  0
  •   Stein Åsmul    10 年前

    我认为有面向服务的方法和面向对象的方法。

    面向服务的方法是创建一系列过滤器,这些过滤器沿着一个不可变的数据传输对象(实体)传递。

    var service1 = new Msi1Service();
    var msi1 = service1.ReadFromFile(sourceMsiPath);
    var service2 = new MsiCustomService();
    var msi2 = service2.Convert(msi1);
    service2.WriteToFile(msi2, targetMsiPath);
    service2.WriteSmsXmlFile(msi2, targetXmlPath);
    

    面向对象的方法可以使用 decorator pattern .

    var decoratedMsi = new CustomMsiDecorator(new MsiFile(sourceMsiPath));
    decoratedMsi.WriteToFile(targetMsiPath);
    decoratedMsi.WriteSmsXmlFile(targetXmlPath);