【问题标题】:Object oriented design面向对象设计
【发布时间】:2011-09-14 10:52:40
【问题描述】:

我有两个 csv 文件 A 和 B。A 是主存储库。我需要读取这些文件,将 B 的记录映射到 A 并将映射的记录保存到另一个文件。 保存记录的类是,比如 Record。保存匹配记录的类是 RecordMatch。

class Record
{
  string Id;
  string Name;
  string Address;
  string City;
  string State;
  string Zipcode;
}

class RecordMatch
{
  string Aid;
  string AName;
  string Bid;
  string BName;
  double NameMatchPercent;
}

映射场景是这样的:首先,针对 B 的每条记录,使用州、城市和邮政编码过滤 A 的记录。然后将这样过滤的 A 的记录与 B 的记录进行比较。这种比较是在名称字段之间进行的,并且是使用模糊字符串算法的最佳匹配比较。选择并保存最佳匹配。

字符串匹配算法会给出匹配百分比。因此,必须选择所有匹配项中最好的结果。

既然我已经尽力解释了这个场景,那么我将进入设计问题。我最初的设计是制作一个 Mapper 类,如下所示:

class Mapper
{
  List<Record> ReadFromFile(File);
  List<Record> FilterData(FilterType);
  void Save(List<Record>);
  RecordMatch MatchRecord(Record A, Record B);
}

但是看看设计,它似乎只是一些方法的类包装器。我没有看到任何面向对象的设计。我也觉得 Match() 比 Mapper 类更属于 Record 类。

但从另一个角度看,我看到这个类实现了类似于存储库模式的东西。

我认为另一种方法是保留 Mapper 类,只需将 Match() 方法移至 Record 类,如下所示:

class Mapper
{
  List<Record> ReadFromFile(File);
  List<Record> FilterData(FilterType);
  void Save(List<Record>);
}

class Record
{
  string id;
  string name;
  string address;
  // other fields;

  public RecordMatch Match (Record record)
  {
    // This record will compare the name field with that of the passed Record.
    // It will return RecordMatch specifyin the percent of match.
  }
}

现在我完全被这个简单的场景弄糊涂了。在这种情况下,什么是理想的 OO 设计?

【问题讨论】:

    标签: oop design-patterns repository-pattern object-oriented-analysis


    【解决方案1】:

    有趣的是,我现在正在做一个几乎完全一样的项目。

    简单回答: 好的,首先,如果一个方法暂时处于错误的类中,这并不是世界末日!如果您的类都包含测试, 函数所在的位置很重要,但可以根据您(您所在领域的王者)认为合适的方式灵活地进行更改。

    如果你测试这个,那么这将是我的第一个建议。许多比我更聪明的人都谈到了 TDD 和测试如何帮助你的课程自然地达到最佳设计。

    更长的答案: 与其寻找应用于设计的模式,我更喜欢这样思考:你的每个类必须改变的原因是什么?如果您将这些原因彼此分开(这是 TDD 可以帮助您做的一件事),那么您将开始看到设计模式自然地从您的代码中出现。

    通过阅读您的问题,我可以想到以下一些改变的原因:

    1. 数据文件更改格式/添加列
    2. 您找到了更好的匹配算法,或者:“现在我们也想过滤手机号码”
    3. 要求您也使其与 xml/yaml/etc 文件匹配
    4. 系统会要求您将其保存为新格式/位置

    好的,所以,如果实现其中任何一个会使您需要在某处添加“if 语句”,那么这可能是实现公共接口的子类的接缝。

    另外,假设您要将创建的文件保存在新位置。这是改变的原因之一,不应与您需要改变合并策略重叠。如果这两个部分在同一个类中,则该类现在有两个职责,这违反了single responsibility principle

    所以,这是一个非常简短的示例,要更深入地了解良好的 OO 设计,请查看SOLID principles。学习这些并寻求在整个 OO 设计中谨慎地应用它们是不会出错的。

    【讨论】:

    • 我从您的简单回答中得到了很好的解脱,因为暂时不用担心事情会出错;-) 是的,我正在测试课程;或许这就是我心中产生疑惑的原因。
    • 我认为是单一责任原则在吞噬我。您对“如果”问题提供了很好的见解。事情在很大程度上变得清晰了。
    【解决方案2】:

    我试了一下。当谈到 OO 原则或设计模式时,我认为你无能为力,除了可能使用 MatchingAlgorithm 的组合(如果需要,可能还有策略和模板)。这是我做的:

        class Mapper {
            map(String fileA, String fileB, String fileC) {
                RecordsList a = new RecordsList(fileA);
                RecordsList b = new RecordsList(fileB);
                MatchingRecordsList c = new MatchingRecordsList();
    
                for(Record rb : b) {
                    int highestPerc = -1;
                    MatchingRecords matchingRec;
    
                    for(Record ra : a) {
                        int perc;
                        rb.setMatchingAlgorithm(someAlgorithmYouVeDefined);
                        perc = rb.match(ra);
                        if(perc > highestPerc) {
                            matchingRec = new MatchingRecords(rb, ra, perc);
                        }
                    }
    
                    if(matchingRec != null) {
                        c.add(matchingRec);
                    }
                }
    
                c.saveToFile(fileC);
            }
        }
    
        class MatchingAlgorithm {
            int match(Record b, Record a) {
                int result;
                // do your magic
                return result;
            }
        }
    
        class Record {
            String Id;
            String Name;
            String Address;
            String City;
            String State;
            String Zipcode;
    
            MatchingAlgorithm alg;
    
            setMatchingAlgorithm(MatchingAlgorithm alg) {
                this.alg = alg;
            }
    
            int match(Record r) {
                int result; -- perc of match
                // do the matching by making use of the algorithm
                result = alg.match(this, r);
                return result;
            }
    
        }
    
        class RecordsList implements List<Record> {
            RecordsList(file f) {
                //create list by reading from csv-file)
            }
        }
    
        class MatchingRecords {
            Record a;
            Record b;
            int matchingPerc;
    
            MatchingRecords(Record a, Record b, int perc) {
                this.a = a;
                this.b = b;
                this.matchingPerc = perc;
            }
        }
    
        class MatchingRecordsList {
            add(MatchingRecords mr) {
                //add
            }
    
            saveToFile(file x) {
                //save to file
            }
        }
    

    (这是用 Notepad++ 编写的,因此可能存在拼写错误等;建议的类也肯定会从更多重构中受益,但如果您选择使用此布局,我会留给您。)

    【讨论】:

    • 发布这个问题后,我开始自己集思广益,因为我有几个小时没有得到答案:P 我想出了一个与你的非常相似的设计;你在我的想法中添加了缺失的部分。 :)
    • 哦,我并没有打算在其中填充设计模式;只是我在设计中错误地看到了类似于存储库模式(读取、查找/过滤、保存操作)的东西。所以我只是提到了它。但我知道那确实是我编造的。我现在要花一些钱在“四人帮”上;-)
    猜你喜欢
    • 1970-01-01
    • 2016-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-27
    • 2015-09-06
    相关资源
    最近更新 更多