重写ruby代码以删除动态变量名称的重复

时间:2017-05-21 05:16:58

标签: ruby oop dry

我正在学习OOD,而且我已经将一些代码重组为课程。我有三个相同的代码片段,用于打开文件并将内容提供给哈希:

# Build grade objects and insert into hash
pass_fail_array = load_csv(pass_fail_file)
grade_collection = pass_fail_array.map{ |e| Grade.new(e) }

grade_hash = {}

grade_collection.each do |x|
    grade_hash[x.mark.to_s] = x
end

# Build student objects and insert into hash
student_array = load_csv(student_file)
student_collection = student_array.map{ |e| Student.new(e) }

student_hash = {}

student_collection.each do |x|
    student_hash[x.full_name] = x
end

# students = {:array_name = "student_array",}

# Build course objects and insert into hash
course_array = load_csv(course_catalog_file)
course_collection = course_array.map{ |e| Course.new(e) }

course_hash = {}

course_collection.each do |x|
    course_hash[x.course.to_s] = x
end

当我第一次尝试将其重写为一种方法时,我不确定如何命名我正在生成的集合 - grade_hashcourse_hash和{{1 }}

我想,可能这应该是一个类,因为这段代码是关于生成集合的副本?看看在这种特定情况下如何应用一般原则真的很有帮助

1 个答案:

答案 0 :(得分:2)

如果解决方案是动态变量名称,那么现在有两个问题。

通常,如果您曾经想要使用动态变量名,那么答案就是哈希,数组或函数。在这种情况下,功能是合适的。使用extract method refactoring

注意:我的Ruby很生疏,为编码错误道歉。我将基本保留算法,因为这不是重点。

从其中一个重复的代码块开始。

# Build grade objects and insert into hash
pass_fail_array = load_csv(pass_fail_file)
grade_collection = pass_fail_array.map{ |e| Grade.new(e) }

grade_hash = {}

grade_collection.each do |x|
    grade_hash[x.mark.to_s] = x
end

通过将其包装在函数声明中并返回代码块的产品,将其转换为函数:grade_hash

def load_from_csv()
    pass_fail_array = load_csv(pass_fail_file)
    grade_collection = pass_fail_array.map{ |e| Grade.new(e) }

    grade_hash = {}

    grade_collection.each do |x|
        grade_hash[x.mark.to_s] = x
    end

    return grade_hash
end

注意哪些变量是在函数外部声明的,只是pass_fail_file所以传递它。

def load_from_csv(file)
    pass_fail_array = load_csv(file)
    grade_collection = pass_fail_array.map{ |e| Grade.new(e) }

    grade_hash = {}

    grade_collection.each do |x|
        grade_hash[x.mark.to_s] = x
    end

    return grade_hash
end

用函数调用替换代码。

grade_hash = load_from_csv(pass_fail_file)

提取方法是删除重复的第一步,重复步骤。

现在我们需要尝试使该功能适用​​于其他情况。每个代码块中只有两个不同的东西......

  1. 为其创建新对象的类。
  2. 将哪个字段放入哈希值。
  3. 第一个很容易,你可以传入类名。这表明这可能是一种类方法。

    第二个有点棘手。你可以传入一个函数,说明如何转换为哈希。但这些是对象,利用。不要告诉班级如何从CSV加载其对象,而是要求班级为您加载CSV中的对象。这意味着绝对要使它成为一种类方法。

    要解决散列键的问题,请定义一个方法,说明如何获取CSV哈希的密钥并使用它。

    # In each class define how to get the key for the CSV
    def csv_key
        return mark.to_s
    end
    
    # In a mixin, put a generic way to load from a CSV
    def self.load_from_csv(file)
        from_csv = load_csv(file)
        objs = from_csv.map{ |e| new(e) }
    
        objs.each do |x|
            hash[x.csv_key] = x
        end
    
        return hash
    end
    

    这种方法可能做得太多,分成两部分。一个用于从CSV加载对象,另一个用于将对象数组转换为哈希值。

    def self.load_from_csv(file)
        return load_csv(file).map{ |e| self.new(e) }
    end
    
    def self.hash_from_objects(objs)
        objs.each do |x|
            hash[x.csv_key] = x
        end
    
        return hash
    end
    

    则...

    grades = Grade.hash_from_objects(
        Grade.load_from_csv(pass_fail_file)
    )
    
    students = Student.hash_from_objects(
        Student.load_from_csv(student_file)
    )
    
    courses = Course.hash_from_objects(
        Course.load_from_csv(course_catalog_file)
    )
    

    这不是一个很棒的界面,但你可以看到它是如何远离你告诉对象做什么的procedural programming,以及你询问对象的对象界面怎么做。

    下一步是真正考虑将加载对象与正在加载的对象分开。

    请注意,该函数几乎不知道它正在加载的对象。这表明下一步是使Factory class从CSV加载对象,而不是将其作为对象接口本身的一部分。 CSV加载器工厂对象将知道CSV文件和类。它将使用类的csv_key方法。

    class CSVLoader
        attr_reader :file, :class
    
        def load
            hash = {}
            load_csv(@file).map{ |e| @class.new(e) }.each do |x|
                hash[ x.csv_key ] = x
            end
    
            return hash
        end
    end
    
    grades = CSVLoader.new( file: pass_fail_file, class: Grade ).load
    students = CSVLoader.new( file: student_file, class: Student ).load
    courses = CSVLoader.new( file: course_catalog_file, class: Course ).load
    

    这是一个非常好的开始。