我正在学习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_hash
,course_hash
和{{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)
提取方法是删除重复的第一步,重复步骤。
现在我们需要尝试使该功能适用于其他情况。每个代码块中只有两个不同的东西......
第一个很容易,你可以传入类名。这表明这可能是一种类方法。
第二个有点棘手。你可以传入一个函数,说明如何转换为哈希。但这些是对象,利用。不要告诉班级如何从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
这是一个非常好的开始。