算 1 个月的那坨代码抽出来
算季度半年一年的时传月数 3612 进去就好了
可以直接在里面写个 lambda 偷个懒
当然强烈赞同楼下补充的,多写注释/写测试也 ok
不能再同意了
lz 应该处于为赋新词强说愁的阶段?毕竟刚学完元编程,想秀一下
我之前刚学会新知识也这样,后面想了下还是少写点这些,是在要写都多写及行注释
所谓的重构前其实也比较简单明了吧,重构不只是代码写少点呀,我刚刚看完元编程那本也老想这样秀,但是后面发现没必要,秀着秀着发现别人看不懂的时候,是你最痛苦的时候,不能偷懒呀。
require 'rspec/mocks'
require 'vip_money'
RSpec.describe 'vip_money' do
subject { vip_money }
let(:sale) do
{
month: {
money: 1600, original_money: 2000, unit: 2000, discount: 0.8,
saving: 0.2, saving_money: 400
},
quarter: {
money: 4000, original_money: 6000, unit: 1666.67, discount: 0.67,
saving: 0.33, saving_money: 2000
},
semi_annual: {
money: 6400, original_money: 12_000, unit: 1333.34, discount: 0.54,
saving: 0.46, saving_money: 5600
},
year: {
money: 11_200, original_money: 24_000, unit: 1166.67, discount: 0.47,
saving: 0.53, saving_money: 12_800
}
}
end
before do
stub_const('Order::TYPE_MONEY', [2000, 5000, 8000, 14_000])
allow(self).to receive(:test_money_discount).and_return(0.8)
end
it { is_expected.to eq sale }
end
require 'active_support/all'
require 'order'
require 'sale_detail'
require 'test_money_discount'
def vip_money
month_money, quarter_money, semi_annual_money, year_money = Order::TYPE_MONEY
user_discount = test_money_discount
sale_detail = SaleDetail.new(month_money, user_discount)
%i[month quarter semi_annual year].index_with do |period|
sale_detail.send("by_#{period}!", binding.local_variable_get("#{period}_money"))
sale_detail.attributes
end
end
class Order
TYPE_MONEY = [2000, 5000, 8000, 14_000].freeze
end
require 'bigdecimal'
require 'active_support/all'
class SaleDetail
attr_reader :monthly, :user_discount, :price, :months
def initialize(monthly, user_discount)
@monthly = BigDecimal(monthly.to_s)
@user_discount = BigDecimal(user_discount.to_s)
end
def attributes
{
money: total.ceil(2),
original_money: original.ceil(2),
unit: unit.ceil(2),
discount: discount.ceil(2),
saving: saving.floor(2),
saving_money: saved.floor(2)
}
end
def by_month!(price, months = 1)
@months = months
@price = BigDecimal(price.to_s)
end
def by_quarter!(price)
by_month!(price, 3)
end
def by_semi_annual!(price)
by_month!(price, 6)
end
def by_year!(price)
by_month!(price, 12)
end
def total
price * user_discount
end
def original
monthly * months
end
def unit
price / months
end
def discount
total / original
end
def saving
1 - discount
end
def saved
original - total
end
end
def test_money_discount
0.8
end
元编程一时爽,后人维护火葬场。
元编程主要用来工具 (lib) 中,CRUD 时最好不要用元编程。
我会把 by_xxx 的参数 price 和 months 放到构造方法的参数列表里,再 new 出 4 个对象分别取 attributes。按照你目前的写法,其它方法会依赖 by_xxx 方法先执行,如果 by 方法不调用,其它方法会出错,这样的对象在使用上不太友好,别人不容易注意到这个约束。
剩余的部分我跟你差不多。另外我会照习惯把 SaleDetail#attributes 之外的方法全部声明为 private,并且不开放 attr_reader。
嗯… 然后我可能不会写那个 %w[month quarter semi_annual year].index_with
我也想写成 Immutable 的,只是没打算投入更多时间。因为非 Immutable,特地没有写成链式的。private 你也提到了,把我对这段代码的顾虑都说出来了哈哈。