两个模型:一个所有者
和一个狗
:
owner.rb
class Owner < ActiveRecord::Base
has_one :dog
end
dog.rb
class Dog < ActiveRecord::Base
belongs_to :owner
end
这是架构:
schema.rb
ActiveRecord::Schema.define(version: 123) do
create_table "dogs", force: true do |t|
t.string "name"
t.integer "energy"
t.integer "owner_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "dogs", ["owner_id"], name: "index_dogs_on_owner_id"
create_table "owners", force: true do |t|
t.string "name"
t.string "energy"
t.datetime "created_at"
t.datetime "updated_at"
end
end
非常简单的设置。
我想要一个主人
带他的狗
去散步。当散步结束时,主人的能量会下降 5,狗的能量会下降 20。
很明显,这个walk_the_dog
Action /方法,无论它在哪里,都会影响两个对象:一个owner
对象和一个dog
对象(当然,这个狗对象恰好与这个所有者相关联)。
我不知道把这段代码放在哪里。我知道我可以简单地在 owners_controller.rb
中创建一个 Action ,但这似乎是个坏主意。它看起来像这样:
owners_controller.rb
class OwnersController < ApplicationController
def walk_the_dog
@owner = Owner.find(params[:id])
@owner.energy -= 5
@owner.dog.energy -= 20 # this line in particular seems like bad OO design
@owner.save
@owner.dog.save
end
...
end
据我了解,对象应该只改变自己的状态,不应该改变其他对象的状态。所以这似乎是个坏主意,因为在所有者 Controller 中,我们不仅要更改 owner
对象的状态,还要更改关联的 dog
对象的状态。
我已阅读有关服务的内容。 walk_the_dog
似乎是服务的绝佳案例,因为据我所知,服务允许对象之间的交互以及多个对象的状态更改。我只是不知道如何去做/实现它。
是否应该有一个名为walk_the_dog
的服务对象?它们是否应该只是服务目录中的一个文件,其中包含一堆服务方法——其中一个称为 walk_the_dog
和 owners_controller.rb
只是在它的 Controller 中使用这个方法?我不知道下一步是什么。
注意:我看到有人说“谁在乎这是否破坏了 OO 设计。就去做吧,如果它有效,它就有效。”不幸的是,这不是一种选择。我现在正在开发的应用程序遵循了这种思维方式。应用程序变得非常大,现在维护起来非常困难。我想在应用程序的重大重新设计中解决这种情况。
最佳答案
如果重构这段代码,我会做以下几件事:
在你的代码中写数字是一件坏事,要么你已经将它们定义为常量,如 ENERGY_PER_WALK_FOR_DOG = 20
或者更好的方法是在 Dog
的表中定义一个字段模型。这样,可以更好地管理和分配这些值。
add_column :dogs, energy_per_walk, :integer, default: 20
add_column :owners, energy_per_walk, :integer, default: 5
我会在 ApplicationController
中创建一个方法类:
def walk(resources = [])
resources.each do |resource|
resource.lose_walk_energy # you can refine it more!
end
end
在文件夹 app/models/concerns 中,我将编写以下模块:
module Walkable
extend ActiveSupport::Concern
# subtract energy_per_walk form the energy saved in db
def lose_walk_energy
self.energy -= self.energy_per_walk
save
end
end
现在,您的方法简化为以下方法:
def walk_the_dog
@owner = Owner.find(params[:id])
walk([@owner, @owner.dog])
end
关于ruby-on-rails - Rails 应用程序的领域驱动设计 : Implementing a service in a basic example,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30785782/