重写 ruby 代码以删除与动态变量名称的重复
Rewrite ruby code to remove repetition with dynamic variable names
我正在学习 OOD,并且一直在将一些代码重组为 classes。我有三段相同的代码,它们打开一个文件并将内容输入哈希:
# 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
和 student_hash
.
我想,这可能应该是 class,因为这段代码是关于生成集合副本的?了解在这种特定情况下如何应用一般原则真的很有帮助
如果解决方案是动态变量名,你现在有两个问题。
一般来说,如果您想使用动态变量名,那么答案是散列、数组或函数。在这种情况下,函数是合适的。使用 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)
提取方法是去除重复的第一个死记硬背步骤。
现在我们需要尝试让该功能适用于其他情况。每个代码块只有两处不同...
- 要为其创建新对象的 class。
- 将哪个字段放入哈希。
第一个很简单,你可以传入class名字。这表明这可能作为一种 class 方法起作用。
第二个有点棘手。您可以传入一个说明如何转换为散列的函数。但这些都是对象,好好利用吧。不要告诉 class 如何从 CSV 加载它的对象,而是让 class 为您从 CSV 加载对象。这意味着绝对使它成为一种 class 方法。
要处理散列键的问题,请定义一个方法,说明如何获取 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 文件和 class。它将使用 class 的 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
这是一个很好的开始。
我正在学习 OOD,并且一直在将一些代码重组为 classes。我有三段相同的代码,它们打开一个文件并将内容输入哈希:
# 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
和 student_hash
.
我想,这可能应该是 class,因为这段代码是关于生成集合副本的?了解在这种特定情况下如何应用一般原则真的很有帮助
如果解决方案是动态变量名,你现在有两个问题。
一般来说,如果您想使用动态变量名,那么答案是散列、数组或函数。在这种情况下,函数是合适的。使用 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)
提取方法是去除重复的第一个死记硬背步骤。
现在我们需要尝试让该功能适用于其他情况。每个代码块只有两处不同...
- 要为其创建新对象的 class。
- 将哪个字段放入哈希。
第一个很简单,你可以传入class名字。这表明这可能作为一种 class 方法起作用。
第二个有点棘手。您可以传入一个说明如何转换为散列的函数。但这些都是对象,好好利用吧。不要告诉 class 如何从 CSV 加载它的对象,而是让 class 为您从 CSV 加载对象。这意味着绝对使它成为一种 class 方法。
要处理散列键的问题,请定义一个方法,说明如何获取 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 文件和 class。它将使用 class 的 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
这是一个很好的开始。