考虑一个抽象概念Tag
,其中有不同种类的标签,比如Topic
和Location
(以及其他),它们除了作为标签之外是不相关的。它们具有相同的基本Tag
属性,但在其他方面有所不同。
一个Topic
概念是基于一个类似的Tag
概念。likeTopic::Update
通常会继承 from Topic::Create
,但这样的操作也需要继承 from Tag::Update
。Ruby 不支持多重继承——Trailblazer 可以支持吗?
Trailblazer 操作通过一个
builds
块支持继承,该块允许它们根据提供的params
哈希的内容实例化一个子类。这适用于基类 (Tag
) 面向公众并且通过基类调用操作的情况。但是,在此示例中,面向公众的类是Topic
子类。操作需要通过子类 (
Topic
) 调用,但其操作基于公共Tag
基类(反向构建器?)。
这是可以通过单继承实现的一种方法(但它说明了这种方法的缺点)...
每种类型的标签都存储在自己的数据库表中,并具有如下 ActiveRecord 类:
class Tag < ActiveRecord::Base
self.abstract_class = true
end
class Topic < Tag; end
Trailblazer 的概念将遵循类似的设计 -Tag
操作将提供基本功能并由更具体的操作 ( Topic
) 子类化。该Tag
操作不会直接使用 -Topic
例如,控制器将使用该Topic
操作。
该Topic
操作继承自Tag
但必须指定其自己的Topic
模型,这似乎只能在每个操作中实现,要求每个操作都明确地进行子类化:
class Topic < Tag
class Create < Tag::Create
model Topic
end
class Update < Tag::Update
model Topic
end
class Delete < Tag::Delete
model Topic
end
end
这样做的一个问题是,在基本操作上定义的合同认为它是 aTag
而不是 a Topic
,这会导致将其用作模型的问题。一个显示问题所在的示例是在单元格的视图中:该Topic
概念有一个单元格,该单元格呈现视图以操纵其对象。它使用 渲染表单simple_form_for
,如下所示:
simple_form_for operation.contract
这不能按预期工作,因为合同认为它是 aTag
并且这打破了形式:
- 它的参数被发送为
params[:tag]
而不是params[:topic]
- 提交按钮的标签是Create Tag而不是Create Topic。
单元格不能使用operation.model
(否则会起作用),因为在提交的操作失败后渲染时它不会看到任何表单错误。
解决此问题的一种方法是明确说明simple_form_for
:
simple_form_for operation.contract, as: :topic, url: topics_path ...
向 中添加属性时会出现另一个问题Topic
,因为这需要扩展Tag
合同。通常的方法是contract do..end
在操作中添加一个块Topic::Create
。出现问题是因为这样的块不会被看到,Topic::Update
并且Topic::Delete
因为它们继承自Tag
对应的而不是继承自Topic::Create
.
另一种方法是子类Topic::Update
操作继承自Topic::Create
. 这将消除指定模型的需要(因为Topic::Create
这样做),但意味着Tag::Update
操作添加的任何内容都将丢失:
class Update < Create
action :update
end
action
需要重新指定,因为不是Tag::Update
继承的,但是因为Topic::Create
是继承的,所以添加的属性Topic::Create
在Topic::Update
.
只要更改仅在一个基类中,这两种样式都可以工作。当两者都发生变化时它会中断,因为 Ruby 不支持多重继承。考虑Delete
通常如下所示的操作:
class Delete < Create
action :find
def process(params)
# validate params and then delete
end
end
如果是这样,Tag::Delete
那么Topic::Delete
可能是
class Delete < Tag::Delete
model Topic
end
或者
class Delete < Create
action :find
end
在前一种情况下Topic::Delete
,将不知道由添加的属性,Topic::Create
而在后一种情况下,Topic::Delete
将缺少在 中定义的process
方法Tag::Delete
。
开拓者概念如何继承另一个概念并能够扩展其业务?