2010-07-07 54 views
5

我的第一個想法是有些事情是這樣的:如何在子類中的方法之前和之後運行代碼?

class AbstractBuilder 
    attr_reader :time_taken 

    def build_with_timer 
    started_at = Time.now 
    build 
    @time_taken = Time.now - started_at 
    end 

    def build 
    raise 'Implement this method in a subclass' 
    end 
end 

class MyBuilder < AbstractBuilder 
    def build 
    sleep(5) 
    end 
end 

builder = MyBuilder.new.build_with_timer 
puts builder.time_taken 

我懷疑有它提供了更好的靈活性,比如理想,我想稱之爲「打造」的MyBuilder的一個實例,而不是一個更好的辦法'build_with_timer'並始終記錄執行時間。

我確實考慮過使用alias_method從初始化或甚至使用模塊mixin代替類繼承,它會覆蓋在中間調用super的構建方法(不知道這是否可行)。在我放下兔子洞之前,我想我會看看是否有一個確定的練習。

+0

爲了澄清我想在一個子類中定義一個已知名稱的方法,該子類在基類中定義的代碼被透明地包裝(之前和之後)。比起說ActiveRecord過濾器,我需要更少的靈活性/開銷。 – Kris 2010-07-08 09:33:46

+0

https://github.com/PragTob/after_do – Kris 2014-05-04 00:01:43

回答

2

我與alias_method玩:

module Timeable 
    def time_methods *meths 
    meths.each do |meth| 
     alias_method "old_#{meth}", meth 

     define_method meth do |*args| 
     started_at = Time.now 
     res = send "old_#{meth}", *args 
     puts "Execution took %f seconds" % (Time.now - started_at) 
     res 
     end 
    end 
    end 

end 

class Foo 
    def bar str 
    puts str 
    end 
end 

Foo.extend Timeable 
Foo.time_methods :bar 
Foo.new.bar('asd') 
#=>asd 
#=>Execution took 0.000050 seconds 
+0

這看起來像它可以工作,我不想要調用time_methods,因爲在我的情況下,這是子類中的一個且只有一個方法,總是具有相同的名稱,我想要將代碼打包。我將嘗試在基類中使用初始化的alias_method和define_method。這是假定當初始化被調用時子類中的方法存在?!? – Kris 2010-07-08 09:20:50

+0

你在那裏迷失了我。如果你想描述你想要解決的問題,這將會有所幫助。首先,你有控制父類嗎?你可以改變'build_with_timer'方法嗎? – 2010-07-08 13:47:55

+0

是的,我控制這兩個類,在子類中會有一個名爲'build'的方法,當調用時在基類中指定的代碼在方法之前和之後運行時,前/後代碼將始終爲一樣。希望這是有道理的。 – Kris 2010-07-12 08:50:34

0

聽起來就像您正在尋找掛鉤到對象生命週期事件。您必須將其構建到您的基礎對象中,並提供一個小小的DSL - 我認爲您是在類似ActiveRecord Callbacks之後。下面是我們如何修改例如允許這樣的事情:

class AbstractBuilder 
    attr_reader :time_taken 

    def construct! # i.e., build, and also call your hooks 
    @@prebuild.each { |sym| self.send(sym) } 
    build 
    @@postbuild.each { |sym| self.send(sym) } 
    end 

    def construct_with_timer 
    started_at = Time.now 
    construct! 
    @time_taken = Time.now - started_at 

    puts "!!! Build time: #@time_taken" 
    end 

    class << self 
    def before_build(fn); @@prebuild ||= []; @@prebuild << fn; end 
    def after_build(fn); @@postbuild ||= []; @@postbuild << fn; end 
    end 
end 

class MyBuilder < AbstractBuilder 
    before_build :preprocess 
    after_build :postprocess 

    def build; puts "BUILDING"; sleep(3); end 
    def preprocess; puts "Preparing to build..."; end 
    def postprocess; puts "Done building. Thank you for waiting."; end 
end 

builder = MyBuilder.new 
builder.construct_with_timer 

# => Preparing to build... 
# => BUILDING 
# => Done building. Thank you for waiting. 
# => !!! Build time: 3.000119 
+0

是和不是,我想要過濾器之前/之後的概念(或周圍),但只會有一種方法,總是使用相同的名稱,我需要以包裝,並將始終包裝在相同的代碼中。所以我需要這個硬編碼版本。從你的例子看來,我需要在類級別存儲前/後代碼,然後在實例化對象後注入它? – Kris 2010-07-08 09:30:23

+0

正確 - 前/後邏輯是快速構建子類的小語言。對於更復雜的需求,請注意,Ruby本身也提供了一些有用的內部回調。您可以編寫名爲「繼承」和「包含」的方法,只要該類繼承或包含,就會觸發這些方法。 – 2010-07-08 17:21:44

4

我有一個刺在一個版本,以達到你想要的東西。該版本不要求子類有任何額外的代碼。

class AbstractBuilder 

    @@disable_override = false 

    def before_method 
    puts "before" 
    end 

    def after_method 
    puts "after" 
    end 

    def self.method_added name 
    unless @@disable_override 
     if name == :build 
     @@disable_override = true # to stop the new build method 
     self.send :alias_method, :sub_build, :build 
     self.send :remove_method, :build 
     self.send :define_method, :build do 
      before_method 
      sub_build 
      after_method 
     end 
     @@disable_override = false 
     else 
     puts "defining other method #{name}" 
     end 
    end 
    end 

end 

class MyBuilder < AbstractBuilder 

    def build 
    puts "starting build" 
    sleep(5) 
    puts "built." 
    end 

    def unnaffected_method 
    # this method won't get redefined 
    end 

end 

b = MyBuilder.new 
b.build 

輸出

defining other method unnaffected_method 
before 
starting build 
built. 
after 
0

這是一本教科書定義使用情況Aspect-Oriented Programming。它通常提供更清晰的關注點分離。在這個舞臺上,Ruby提供了AquariumAspectR。但是,您可能不想向項目添加其他依賴項。因此,您可能仍然考慮使用其他方法之一。

相關問題