2009-03-03 23 views
16

Tôi đến từ nền C# và đã bắt đầu lập trình bằng Ruby. Vấn đề là, tôi cần phải biết làm thế nào tôi có thể nâng cao sự kiện trong lớp học của tôi để các nhà quan sát khác nhau có thể được kích hoạt khi mọi thứ cần phải xảy ra.Làm cách nào để thực hiện các sự kiện trong Ruby?

Vấn đề là sách tôi có trên Ruby thậm chí không đề cập đến các sự kiện, hãy để một mình cung cấp các ví dụ. Có ai có thể giúp tôi không?

Trả lời

4

Tôi đã thử viết một thư viện GUI trong Ruby với một chút C và chủ yếu là Ruby. Nó đã kết thúc rất chậm tôi đã từ bỏ và không bao giờ phát hành nó. Nhưng tôi đã viết một hệ thống sự kiện cho nó mà tôi đã cố gắng để làm cho dễ dàng hơn so với C#. Tôi viết lại nó một vài lần để làm cho nó dễ sử dụng hơn. Tôi hy vọng nó có phần hữu ích.

class EventHandlerArray < Array 
    def add_handler(code=nil, &block) 
    if(code) 
     push(code) 
    else 
     push(block) 
    end 
    end 
    def add 
    raise "error" 
    end 
    def remove_handler(code) 
    delete(code) 
    end 
    def fire(e) 
    reverse_each { |handler| handler.call(e) } 
    end 
end 

# with this, you can do: 
# event.add_handler 
# event.remove_handler 
# event.fire (usually never used) 
# fire_event 
# when_event 
# You just need to call the events method and call super to initialize the events: 
# class MyControl 
# events :mouse_down, :mouse_up, 
#   :mouse_enter, :mouse_leave 
# def initialize 
#  super 
# end 
# def when_mouse_up(e) 
#  # do something 
# end 
# end 
# control = MyControl.new 
# control.mouse_down.add_handler { 
# puts "Mouse down" 
# } 
# As you can see, you can redefine when_event in a class to handle the event. 
# The handlers are called first, and then the when_event method if a handler didn't 
# set e.handled to true. If you need when_event to be called before the handlers, 
# override fire_event and call when_event before event.fire. This is what painting 
# does, for handlers should paint after the control. 
# class SubControl < MyControl 
# def when_mouse_down(e) 
#  super 
#  # do something 
# end 
# end 
def events(*symbols) 
    # NOTE: Module#method_added 

    # create a module and 'include' it 
    modName = name+"Events" 
    initStr = Array.new 
    readerStr = Array.new 
    methodsStr = Array.new 
    symbols.each { |sym| 
    name = sym.to_s 
    initStr << %Q{ 
     @#{name} = EventHandlerArray.new 
    } 
    readerStr << ":#{name}" 
    methodsStr << %Q{ 
     def fire_#{name}(e) 
     @#{name}.fire(e) 
     when_#{name}(e) if(!e.handled?) 
     end 
     def when_#{name}(e) 
     end 
    } 
    } 
    eval %Q{ 
    module #{modName} 
     def initialize(*args) 
     begin 
      super(*args) 
     rescue NoMethodError; end 
     #{initStr.join} 
     end 
     #{"attr_reader "+readerStr.join(', ')} 
     #{methodsStr.join} 
    end 
    include #{modName} 
    } 
end 

class Event 
    attr_writer :handled 
    def initialize(sender) 
    @sender = @sender 
    @handled = false 
    end 
    def handled?; @handled; end 
end 
-1

Tôi không chắc chắn chính xác những gì bạn muốn nói nhưng có thể bạn có thể sử dụng ngoại lệ trong các lớp học của mình và tăng chúng lên một số "sự kiện" nhất định. Nếu bạn cần sự kiện để phát triển GUI thì hầu hết các khung công tác GUI đều định nghĩa kiểu xử lý sự kiện của riêng chúng.

Hy vọng câu trả lời này phần nào bạn đang đặt câu hỏi.

+0

Tôi muốn sử dụng các mẫu Observer Thiết kế. – Ash

21

Câu hỏi đã được trả lời, nhưng có một số observer được tích hợp ngay vào thư viện chuẩn nếu bạn muốn cung cấp cái nhìn đó. Tôi đã sử dụng nó trong quá khứ cho một dự án trò chơi nhỏ, và nó hoạt động rất tốt.

3

Trình nghe Ruby cực kỳ đơn giản. Đây không phải là sự thay thế chính xác cho các sự kiện .NET, nhưng đây là một ví dụ cực kỳ đơn giản của một người nghe rất đơn giản.

module Listenable 

    def listeners() @listeners ||= [] end 

    def add_listener(listener) 
     listeners << listener 
    end 

    def remove_listener(listener) 
     listeners.delete listener 
    end 

    def notify_listeners(event_name, *args) 
     listeners.each do |listener| 
      if listener.respond_to? event_name 
       listener.__send__ event_name, *args 
      end 
     end 
    end 

end 

Cách sử dụng:

class CowListenable 
    include Listenable 

    def speak 
     notify_listeners :spoken, 'moooo!' 
    end 

end 

class CowListener 

    def initialize(cow_listenable) 
     cow_listenable.add_listener self 
    end 

    def spoken(message) 
     puts "The cow said '#{message}'" 
    end 

end 

cow_listenable = CowListenable.new 
CowListener.new(cow_listenable) 
cow_listenable.speak 

Output:

The cow said 'moooo!' 
+1

Câu trả lời này cảm thấy ruby-ish nhất. Nó dựa vào tính chất động của ngôn ngữ chứ không phải là mô phỏng một ngôn ngữ được đánh máy mạnh mẽ, nó đơn giản để sử dụng và hiểu, và nó hỗ trợ multicast. –

1

Tiết lộ: Tôi là nhà duy trì của event_aggregator đá quý

Tùy thuộc vào cách bạn muốn tiếp cận vấn đề bạn có khả năng có thể sử dụng một tập hợp sự kiện ator. Bằng cách này bạn có thể xuất bản các tin nhắn của một loại nhất định và sau đó để đối tượng của bạn lắng nghe các loại bạn muốn chúng nhận được. Điều này có thể trong một số trường hợp nhất định sẽ tốt hơn so với các sự kiện bình thường bởi vì bạn nhận được một khớp nối rất lỏng giữa các đối tượng của bạn. Nhà sản xuất và người nghe sự kiện không cần chia sẻ một tham chiếu đến người khác.

Có một viên ngọc giúp bạn với điều này được gọi là event_aggregator. Với nó, bạn có thể làm như sau:

#!/usr/bin/ruby 

require "rubygems" 
require "event_aggregator" 

class Foo 
    include EventAggregator::Listener 
    def initialize() 
     message_type_register("MessageType1", lambda{|data| puts data }) 

     message_type_register("MessageType2", method(:handle_message)) 
    end 

    def handle_message(data) 
     puts data 
    end 

    def foo_unregister(*args) 
     message_type_unregister(*args) 
    end 
end 

class Bar 
    def cause_event 
     EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3]).publish 
    end 
    def cause_another_event 
     EventAggregator::Message.new("MessageType2", ["Some More Stuff",2,3]).publish 
    end 
end 

f = Foo.new 

b = Bar.new 
b.cause_event 
b.cause_another_event 
# => Some Stuff 
    2 
    3 
# => Some More Stuff 
    2 
    3 

Hãy nhận biết rằng nó là async theo mặc định, vì vậy nếu bạn thực hiện chỉ kịch bản này kịch bản có thể thoát ra trước khi các sự kiện được thông qua. Để vô hiệu hóa async sử dụng hành vi:

EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3], false).publish 
#The third parameter indicates async 

Hy vọng rằng điều này có thể hữu ích trong trường hợp của bạn

Các vấn đề liên quan