OO风格 - 简单的问题

时间:2009-08-17 04:22:20

标签: c# oop

对于所有务实的面向对象的人来说,这是一个简单的问题。 我已多次阅读以避免类似“处理器”和“xxxxHandler”这样的类,以便同意OO标准:我认为这是系统代码可理解性的一个很好的衡量标准。

假设我们有一个扫描某些文件结构的软件,比方说一堆特定的CSV文件。假设我们有一个名为CsvParser的独立模块。

class CsvParser {
    public string GetToken(int position) { .. }
    public bool ReadLine() { .. }
}

class MyCsvFile {
    public string FullPath { get; }

    public void Scan() {
        CsvParser csvp(FullPath);
        while (csvp.ReadLine())
        {
            /* Parse the file that this class represents */
        }
    }
}

这将节省一个“FileScanner”类,它是一个-Processor类型的类。收集的东西说,来自目录的一堆文件,并扫描每个。

class MyFileScan {
    public string[] Files { get; set; }

    public void GetFiles() { this.Files = Directory.GetFiles(..); }

    public void ScanFiles() {
        foreach (string thisFilePath in Files)
        {
           CsvParser csvp(thisFilePath);
           /* ... */
        }
    }
}

OO方法规定了MyCsvFile类,然后是表示对象操作的方法。

有什么想法?你是程序员的想法。

3 个答案:

答案 0 :(得分:3)

我同意你的观点,但如果是我,我可能会称之为CsvFile类,除了Scan之外还有一个Parse方法。在面向对象编程中,总是希望让你的类代表“事物”(英语名词)。

除此之外,如果我被要求维护你的代码,我会掌握CsvParser类可能会做什么,而MyFileScan会让我陷入愤怒之中,导致我必须阅读代码来解决它。< / p>

答案 1 :(得分:1)

这是问题域与解决方案域设计。

为了解决问题,我们可以设计我们的类来模拟现实生活对象,即根据问题域的程序。

另一种编程方式是根据Solution Domain进行设计。

例如,当我们设计飞行预订系统时,对于飞行管理专家,他们会将飞行旅行描述为“路线”,“时间”,“角度”(我真的不记得这个术语)。如果我们根据这些模型进行设计,则根据问题域称为设计。

我们也可以使用坐标系(x,y,z)进行设计,因为我们觉得作为程序员,我们可以更有效地处理这些问题。这是解决方案域的设计。

解决方案域的问题是,在项目的世界中,一个不变的是 - 更改!要求总会改变!如果要求发生变化,您必须重新设计程序。

但是,如果将类建模为真实对象,则受变化的影响较小,因为现实对象很少会发生变化。

“处理器”和“xxxxHandler”&lt; - 这是针对解决方案域的设计。

你可以看一下Domain-Driven Design --- DDD for short。

答案 2 :(得分:1)

我认为你所描述的是对象应该处理只需要自己的操作,这通常是一个很好的规则。 “处理器”类没有任何问题,只要它“处理”一些不同(但相关)的东西。但是如果你有一个只处理一件事的类(比如CSV解析器只解析CSV),那么处理器处理的东西就没有理由不对它自己进行处理。

然而,有一个共同的理由违反这条规则:通常你不想做你不必做的事情。例如,对于您的CSV类,如果你想要的只是在CSV中找到第一个单元格为“Bob”的行并获取该行中的第三列(也就是Bob的出生日期),那么你就不要我想读取整个文件,解析它,然后搜索你刚创建的漂亮的数据结构:它效率低下,特别是如果你的CSV有100K行,Bob的条目在第5行。

您可以重新设计CSV类以对CSV进行小规模操作,例如跳到下一行并获取第一个单元格。但是现在你正在实现你不会真正谈论CSV的方法。 CSV不读取行,它们存储它们。他们没有找到细胞,他们只是拥有它们。此外,如果你想进行大规模的操作,比如读取整个CSV并按第一个单元格排序,那么你会希望你在整个文件中读取旧的方法,解析它,然后翻过来。您创建的整个数据结构。您可以在同一个类中执行这两个操作,但现在您的类实际上是两个类,用于两个不同的目的。你的班级已经失去了凝聚力,你创造的任何班级实例都会有两倍的行李,而你只能使用一半。

在这种情况下,对CSV(对于大规模操作)和低级操作的“处理器”类进行高级抽象是有意义的。 (以下是用Java编写的,因为我知道比我更了解C#):

public class CSV
{
    final private String filename;
    private String[][] data;
    private boolean loaded;

    public CSV(String filename) { ... }

    public boolean isLoaded() { ... }
    public void load() { ... }
    public void saveChanges() { ... }
    public void insertRowAt(int rowIndex, String[] row) { ... }
    public void sortRowsByColumn(int columnIndex) { ... }

    ...
}

public class CSVReader
{
    /*
     * This kind of thing is reasonably implemented as a subclassable singleton
     * because it doesn't hold state but you might want to subclass it, perhaps with
     * a processor class for another tabular file format.
     */
    protected CSVReader();
    protected static class SingletonHolder
    {
        final public static CSVReader instance = new CSVReader();
    }

    public static CSVReader getInstance()
    {
        return SingletonHolder.instance;
    }

    public String getCell(String filename, int row, int column) { ... }
    public String searchRelative(String filename,
        String searchValue,
        int searchColumn,
        int returnColumn)
    { ... }

    ...
}

类似的众所周知的例子是SAX和DOM。 SAX是低级,细粒度的访问,而DOM是高级抽象。