2017-05-21 32 views
1

我正在学习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 

当我第一次尝试重写这是一个方法,我不知道如何命名我”集合m生成 - grade_hash,course_hashstudent_hash

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

回答

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. 将哪个字段放入散列。

第一个很容易,你可以传入类名。这表明这可能是一种类方法。

第二个有点棘手。你可以传入一个函数来说明如何转换为散列。但是这些都是对象,利用。不要告诉该类如何从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 

这是一个很好的开始。

+0

非常感谢,这非常有帮助。我可以看到你是如何一步一步完成的,并且你所链接的资源非常棒。我可以开始看到如何思考界面的应用。起初我很困惑加载行为是否应该被认定为一个类,因为我不认为我想要它的多个实例,但是你通过它的方式可以帮助我更多地了解如何应用'工厂类'模式,同样感谢你 – zqe