如果解决方案是动态变量名,那么您现在有两个问题。
一般来说,如果您想使用动态变量名,答案是哈希、数组或函数。在这种情况下,函数是合适的。使用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
这是一个很好的开始。