目前,我正致力于将数据从表单服务解析为pdf表单。我创建了两个类,一个继承了另一个类。但是,我看到我创建的类在代码行中不断增长,我担心这些类变得很难维护。
我想征求社区对OOP原则、风格违规和最佳实践的反馈意见。如果也存在安全问题,就指出这些问题。下面是使用继承的类。
require 'open-uri'
class FillablePdfForm
attr_writer :template_path
attr_reader :attributes
def initialize
fill_out
end
def export(file_name, output_file_path: nil)
output_file_path ||= "#{Rails.root}/tmp/pdfs/#{application_date}_#{which_application}_#{what_status}_#{file_name}.pdf"
pdftk.fill_form template_path, output_file_path, attributes
output_file_path
end
def form_fields
pdftk.get_field_names template_path
end
def template_path
pdf_form_application
@template_path ||=
"#{Rails.root}/public/pdfs/#{which_application.downcase}_#{what_status.downcase}.pdf"
end
protected
def application_date
Time.now.strftime('%Y-%m-%d')
end
def pdf_form_application
File.open("#{Rails.root}/public/pdfs/#{which_application.downcase}_#{what_status.downcase}.pdf", "wb") do |file|
file << URI.parse(url_of_pdf_form).read
end
end
def url_of_pdf_form
@form_fields.find do |field|
field['label'] == "#{which_application}_#{what_status}_URL"
end['default']
end
def attributes
@attributes ||= {}
end
def fill(key, value)
attributes[key.to_s] = value
end
def pdftk
@pdftk ||= PdfForms.new
end
def fill_out
raise 'Must be overridden by child class'
end
end另外,我正在传递构造函数FormStack::Form.new,但我想知道是否应该将它作为参数传递。
class PdfScrie < FillablePdfForm
def initialize(user_submission_data, form_fields)
@user_submission_data = user_submission_data
@form_fields = form_fields
@formstack = FormStack::Form.new
super()
end
private
# PDF Constants
VALUE_CHECKBOX_ON = 'On'.freeze
OPTION_SEP = ' | '.freeze
LABEL_APPLICATION = 'APPLICATION'.freeze
LABEL_STATUS = 'STATUS'.freeze
def fill_out
form_fields.each do |field| # PDF form fields
unless dictionary[field]
Rails.logger.warn "#{self.class.name}: Missing \"#{field}\" mapping."
next
end
id = dictionary[field].split(OPTION_SEP)[0]
@user_submission_data
.select { |field_data| field_data[FormStack::Form::ATTR_FIELD_ID] == id }
.each { |field_data| fill_form_with_data(field, field_data) }
end
end
def fill_form_with_data(field, field_data)
field_number = field_data[FormStack::Form::ATTR_FIELD_ID]
value = field_data[FormStack::Form::ATTR_FIELD_VALUE]
field_type = FormStack::Form::field_type(@form_fields, field_number)
self_method = "fill_#{field_type}".to_sym
if self.respond_to?(self_method, :include_private)
send(self_method, field_number, field, value)
else
fill(field, value)
end
end
# Field Type Methods
def fill_address(field_number, field, value)
address_by_section = FormStack::Form.parse_formstack_nested_attrs(value)
address_by_section.each do |section, value|
fill(field, value) if form_field_has_section?(field, section) ||
FormStack::Form::address_section_aparment?(field, section)
end
end
def fill_phone(field_number, field, value)
parse_phone_number(value)
fill(field, @phone_number_sections.shift)
end
def fill_name(field_number, field, value)
full_name = FormStack::Form::parse_name(value)
fill(field, full_name)
end
def fill_checkbox(field_number, field, value)
if FormStack::Form::field_is_grouped_checkbox(@form_fields, field_number)
FormStack::Form::parse_checked_options(value).each do |option|
fill(field, VALUE_CHECKBOX_ON) if checked_option_matches_value(field, option)
end
else
fill(field, value)
end
end
# END Field Type Methods
# Helpers
def checked_option_matches_value(field, option)
dictionary[field].split(OPTION_SEP)[1].include?(option)
end
def parse_phone_number(phone_number)
if phone_number_sections_empty?
@phone_number_sections = FormStack::Form::parse_phone(phone_number)
end
end
def phone_number_sections_empty?
@phone_number_sections.nil? || @phone_number_sections.empty?
end
def form_field_has_section?(form_field_name, address_section)
form_field_name.include? address_section.upcase
end
def dictionary
@dictionary ||= JSON.parse(find_dictionary['section_text'])
end
def find_dictionary
@formstack.find_field_by_label("#{which_application}_#{what_status}_DICTIONARY",
@form_fields)
end
def which_application
@formstack.find_value_by_label(LABEL_APPLICATION,
@form_fields,
@user_submission_data)
end
def what_status
@formstack
.find_value_by_label(LABEL_STATUS, @form_fields, @user_submission_data)
end
end可以自由地指出改进、反馈、最佳实践和资源的领域。
发布于 2019-03-03 11:12:51
以类比的方式提出的客观建议:
考虑一下Document和Cabinet。Document应该知道如何将自己的副本放在Cabinet中吗?或者Cabinet应该知道如何将文档复制到自己中?
在一个小程序中,两者都可能是好的。然而,随着系统变得越来越复杂,随着更多的交互方式的增加,它将变得不可维护。
当出现这种情况时,应该有一个处于更高抽象级别的参与者,例如“秘书”,它复制一个副本(可能通过Document#copy请求)并将副本归档到内阁(也许是通过请求Cabinet#file的副本)。在各自的孤立上下文中,它们不需要交互或相互了解,因此它们的实现将不包含对彼此的引用。
如果只有“一个秘书”,就把行动放在最高级的抽象上--主程序。随着复杂性的增加,也许可以定义一个Secretary类。
然而,请记住,Secretary的S行为是更高的抽象,而Document是一个较低的抽象。依赖方向性很重要。Document不应该强迫Secretary去行动。
适用于您的代码的
export FillablePdfForm是文档PdfForms是内阁FillablePdfForm#export是把自己放进内阁的文件fill_form_with_data field_data和FormStack::Form是文档PdfScrie是内阁PdfScrie#fill_form_with_data是内阁把文件放在自己的地方另一个问题是FillablePdfForm#template_path在哪里调用which_application,它是在Scrie子类中实现的,L在实心中讨论了这个问题。
虽然维基百科的文章有点厚,但谷歌到处寻找每一条坚实原则的替代解释。
https://codereview.stackexchange.com/questions/209536
复制相似问题