這些問題已放在最后,因為如果沒有背景關系,它們可能不清楚。
目的
這個想法是,我們可以在整個客戶端類 ( > ) 的繼承鏈中動態繼承和Foo重新定義它的一些類依賴項。下面一個嘗試反映這個想法:BarBaz
Foo
▼
Bar.foo --> FooChild <id: Bar.id)
▼ ?
Baz.foo --> FooChild <id: Baz.id)
這種方法旨在Foo通過改變類的依賴來重用類id。因此,根據使用的客戶端類Foo(或其子類),Foo.id將參考不同的String(由Barand重新配置Baz)。
為清楚起見,請參見下面一個看似更具體的示例,其類比如下:Collectionis to Fooas Roomis toBar和Libraryto Baz。如果要放心我們提到Foo.id,在這種情況下我們提到Collection.item_class; 其類變數Collection將有助于檢索實體物件的專案的類。Collection
Collection
▼
Room.things --> CollectionChild <item_class: Thing)
▼ ?
Library.things --> CollectionChild <item_class: Book)
我們只想在父類( ) 中定義things方法 ( foo)一次。而不是重新定義類( )中的( )方法,目的是動態地重新定義。RoomBarthingsfooLibraryBazitem_class
例子
下面的代碼以某種方式綜合了問題的背景關系:
class Foo
class << self
attr_accessor :id
def resolved_id
id_class, id_method = id.first
id_class.send(id_method)
end
end
def identify
puts "Referring to '#{self.class.resolved_id}'"
end
end
class Bar
class << self
def embed(method, klass:, data:)
define_method "#{method}" do
Class.new(klass) {|child| child.id = data}.new
end
end
def id; "bar"; end
end
embed :foo, klass: Foo, data: {self => :id}
end
class Baz < Bar
def self.id; "baz"; end
end
Baz.new.foo.identify
輸出是:
Referring to 'bar'
我寧愿實作:
Referring to 'baz'
解釋
Bar定義一個動態類方法foo,該方法創建一個子類Foo(我們稱之為FooChild),其中使用對類方法FooChild.id的參考進行初始化。Bar.id- while
Foo和Bar是不屬于同一繼承鏈的類,Baz是子類,Bar并重新定義了類方法id。
問題
雖然從同一繼承鏈中解決依賴關系變得容易(即重新定義方法,例如Bar.idin Baz.id)。要解決來自非父類的依賴關系,需要將參考傳遞給當前類(請參閱data: {self => :id}),而不僅僅是 sym 方法(:id),因為該方法可能不存在或在使用此依賴關系的類中可能不相關。
與僅僅明確地重新定義事物相比,而不是試圖使一切都可配置和可重用,這可能是整個方法不正確并增加了不必要的復雜性。可配置的類可以被視為兩者:適度使用時的時間救星和過度使用時的例外和反模式。
當你越界時,很難看到。
我試圖避免的解決方案
foo總的來說,如果不像這樣在子類中重新定義,似乎不太可能解決Baz這個問題:
class Baz < Bar
def self.id; "baz"; end
embed :foo, klass: Foo, data: {self => :id}
end
Bar.new.foo.identify
Baz.new.foo.identify
輸出:
Referring to 'bar'
Referring to 'baz'
但是,如果是這樣的話,那一行看起來與父類中的完全一樣Bar;只是那self將指的是不同的類。
問題
- 有沒有辦法提供對
:idfromBarto的參考FooChild(當方法foo生成時),可以從子類重新定義Baz?(沒有foo在子類中重新定義)。 - 在瀏覽了一些關于constant lookup and resolution
Rails的帖子后,我很想聽到一些“更簡單”但有效的替代方法。
提前致謝。
uj5u.com熱心網友回復:
你似乎有點過于復雜了。該問題源于self對embed.Bar
由于該 self 恰好在 的類級別上Bar,因此在評估行的那一刻被凍結。Bar
如果您在foo方法中呼叫該 self ,它會在運行時進行評估,并且將是呼叫該方法的任何類。
class Bar
class << self
def id; "bar"; end
end
def foo
klass = self.class
Class.new(Foo) {|child| child.id = {klass => :id}}.new
end
end
其他一切不變。
Baz.new.foo.identify #=> Referring to 'baz'
uj5u.com熱心網友回復:
@Siim_Liiser 答案是關鍵解決方案。添加此答案只是為了使他與問題的原始代碼保持一致:
帶有注釋的修改行:
class Bar
class << self
def embed(method, klass:, data:)
define_method "#{method}" do
# we rather capture the referrer class from the instance object
referrer = self.class
Class.new(klass) {|child| child.id = {referrer => data}}.new
end
end
def id; "bar"; end
end
# from the referrer class, we just pass the reference :id
embed :foo, klass: Foo, data: :id
end
其余代碼保持不變。
Bar.new.foo.identify
Baz.new.foo.identify
輸出:
Referring to 'bar'
Referring to 'baz'
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/507557.html
