您将如何使用冗长的重复方法来识别和修复以下代码异味?这是下面的代码气味:
代码异味
class TransactionProcessingService
def initialize(user, product_id)
@user = user
@product = Actions::Base.find_by id: product_id
end
def call
return false unless valid?
ActiveRecord::Base.transaction do
@reservation = @user.reservations.
where(action_id: @product.id).
where("status IS NOT NULL AND status NOT IN ('archived', 'cancelled')").
first_or_initialize
@reservation.update(quantity: (@reservation.quantity.presence || 0) + 1) # Nil guard
update_state!
end
end
protected
def valid?
@product.allowed_for?(@user) and @user.balance >= @product.price
end
def update_state!
if @product.is_a?(Actions::Target)
@user.transactions.create(status: 'sold_target', transaction_type: :product_purchase, amount: @product.price, product: @product)
@user.update_columns(balance: @user.balance - @product.price - @product.discount_for_user(@user))
elsif @product.is_a?(Actions::Lease)
@user.transactions.create(status: 'sold_lease', transaction_type: :product_purchase, amount: @product.price, product: @product)
@user.update_columns balance: @user.balance - @product.price
end
end
end
但是,我尝试重构它,但是我觉得我变得更糟。您认为我应该如何最好地重构上面的代码?这是我下面所做的:
这是我所做的...
class TransactionProcessingService
def initialize(user, product_id)
@user = user
@product = Actions::Base.find_by id: product_id
end
def call
false unless valid?
ActiveRecord::Base.transaction do
update_user_reservations
create_state
update_balance
end
end
protected
def valid?
@product.allowed_for?(@user) && (@user.balance >= @product.price)
end
def wrapper_around(*)
@product.is_a?
end
def update_user_reservations
result = @user.reservations
.where(action_id: @product.id)
.where("status IS NOT NULL AND status NOT IN ('archived', 'cancelled')").first_or_initialize
result.update(quantity: (quantity.presence || 0) + 1)
end
def update_balance
value = @user.balance - @product.price
target = wrapper_around(Actions::Target)
lease = wrapper_around(Actions::Lease)
@user.update_columns(if target
{ balance: value - @product.discount_for_user(@user) }
elsif lease
{ balance: value }
end)
end
def create_state
product_price = @product.price
target = wrapper_around(Actions::Target)
lease = wrapper_around(Actions::Lease)
@user.transactions.create(if target
{
status: 'sold_target',
transaction_type: :product_purchase,
amount: product_price,
product: product_params
}
elsif lease
{
status: 'sold_lease',
transaction_type: :product_purchase,
amount: product_price,
product: product_params
}
end)
end
end
答案 0 :(得分:3)
只是为了给您一个主意,您可以将代码中可以识别的不同操作与其他一些类分开:
TransactionProcessing:
class TransactionProcessing
def initialize(product_id:, user:)
@user = user
@product_id = product_id
end
def call
return unless product
return unless valid?
ActiveRecord::Base.transaction do
UpdateReservation.new(product: product, user: user).update
update_state!
end
end
private
attr_reader :product_id, :user
delegate :id, :price, to: :product
delegate :quantity, to: :reservation
delegate :balance, :reservations, :transactions, to: :user
def product
Actions::Base.find_by(id: product_id)
end
def update_state!
Object.const_get("#{product.class}Transaction").new(product: product, user: user).update
end
def valid?
product.allowed_for?(user) && balance >= price
end
end
UpdateReservation:
class UpdateReservation
def initialize(user:, product:)
@product = product
@user = user
end
def update
reservation.update(quantity: reservation_quantity)
end
private
attr_reader :user
delegate :id, to: :product
delegate :reservations, to: :user
delegate :quantity, to: :reservation
def reservation
reservations.where(action_id: id).where.not(status: nil)
where('status NOT IN ("archived", "cancelled")').first_or_initialize
end
def reservation_quantity
(quantity.presence || 0) + 1 # Just if quantity.presence can be nil. Otherwise add a rescue.
end
end
动作模块:
module Actions
class CustomTransaction
attr_reader :product, :user
delegate :price, to: :product
delegate :balance, :transactions, to: :user
def initialize(product:, user:)
@product = product
@user = user
end
def update
create_transaction
update_balance
end
def create_transaction
transactions.create(params)
end
def update_balance
user.update(balance: balance_value)
end
private
def params
{ transaction_type: :product_purchase, amount: price, product: product }
end
end
class LeaseTransaction < CustomTransaction
STATUS = 'sold_lease'.freeze
private_constant :STATUS
private
def params
super.merge(status: STATUS)
end
def balance_value
balance - price - discount_for_user
end
def discount_for_user
product.discount_for_user(user)
end
end
class TargetTransaction < CustomTransaction
STATUS = 'sold_target'.freeze
private_constant :STATUS
private
def params
super.merge(status: STATUS)
end
def balance_value
balance - price
end
end
end
您可以看到:
product_id
,避免添加更多代码。 product
属于单独的私有方法。is_a?
。只需打电话。即使这需要改进,所以任何人都可以自在。