簡體   English   中英

Ruby中類似Java的高級枚舉

[英]Advanced Java-like enums in Ruby

首先,這不是Ruby枚舉的重復:)

該問題的公認答案表明這是在Ruby中表示枚舉的一種好方法:

class Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end

在Java中,可以將多個值和方法附加到枚舉值。 我想在Ruby中實現相同或類似的東西。

什么是最類似Ruby的方式來表示這個Java枚舉:

public enum Enum

    VALUE_1("Value 1"),
    VALUE_2("Value 2"),
    VALUE_3("Value 3");

    Enum(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
    private String value;
}  

編輯:

我還想保留Java枚舉的隱含功能:

  • ...檢索序數值
  • ...在枚舉值上調用方法(或類似的東西)

例子:

Enum.VALUE_1.getValue(); // "Value 1"
Enum.VALUE_2.name();     // "VALUE_2"
Enum.VALUE_3.ordinal();  // 2
class MyEnum
  attr_accessor :value
  def initialize(value)
    @value = value
  end

  VALUE1 = new("Value 1")
  VALUE2 = new("Value 2")

  class << self
    private :new
  end
end

MyEnum::VALUE2 # Enum with value "Value 2"
MyEnum.new # Error

一個更精細的解決方案,允許您定義任意“枚舉類”,並為您提供ordinal()

def enum(*values, &class_body)
  Class.new( Class.new(&class_body) ) do
    attr_reader :ordinal

    def initialize(ordinal, *args, &blk)
      super(*args, &blk)
      @ordinal = ordinal
    end

    values.each_with_index do |(name, *parameters), i|
      const_set(name, new(i, *parameters))
    end

    class <<self
      private :new
    end
  end
end

# Usage:
MyEnum = enum([:VALUE1, "Value 1"], [:VALUE2, "Value 2"]) do
  attr_reader :str
  def initialize(str)
    @str = str
  end
end

MyEnum::VALUE1.str #=> "Value 1"
MyEnum::VALUE2.ordinal #=> 1

您始終可以創建類似於Java版本的系統:

module Foo
  class Value
    attr_reader :value

    def initialize(value)
      # Save a frozen, immutable copy
      @value = value.dup.freeze
    end

    # Patch in methods to make it appear more friendly and string-like
    alias_method :to_s, :value
    alias_method :inspect, :value
  end

  # Define constants
  BAR = Value.new('bar')
  BAZ = Value.new('baz')
  BIZ = Value.new('biz')
end

puts Foo::BAR
# => bar

這是我建議的代碼......

EnumModule.rb

module EnumModule
  CONVERT_PROC = Proc.new do
    @values = constants.collect{|c| const_get(c)}.freeze

    @values.each_with_index do |value, idx|
      the_symbol = constants.find{|c| const_get(c) == value}
      sig = class << value ; self end
      sig.send :define_method, :name, proc{the_symbol}
      sig.send :define_method, :ordinal, proc{idx}

      if value.is_a? Hash
        value.each do |k, v|
          sig.send :define_method, k, (v.is_a?(Proc) ? v : proc{v})
        end
      end
      value.freeze
    end

    class << self
      alias :value_of :const_get
    end

    module_function
    def each
      @values.each { |v| yield v }
    end
    def values
      @values
    end
    extend Enumerable

    freeze
  end

  def self.extended extending_obj
    extending_obj.module_eval &CONVERT_PROC
  end
end

SampleEnum.rb

require 'EnumModule'

module SampleEnum
  VALUE_1 = {
    to_s: 'Value_1_str',
    get_value: proc{'Value 1'}
  }

  VALUE_2 = {
    to_s: 'Value_2_str',
    get_value: proc{'Value 2'}
  }

  extend EnumModule
end

#defined method
p SampleEnum::VALUE_1.get_value #=> "Value 1"
p SampleEnum::VALUE_2.get_value #=> "Value 2"

p SampleEnum::VALUE_1.to_s      #=> "Value_1_str"

#name (returns the symbol of the constant)
p SampleEnum::VALUE_1.name #=> :VALUE_1
p SampleEnum::VALUE_2.name #=> :VALUE_2

#ordinal
p SampleEnum::VALUE_1.ordinal #=> 0
p SampleEnum::VALUE_2.ordinal #=> 1

#emulates Java Enum's valueOf(is an alias of const_get)
p SampleEnum.value_of('VALUE_1').get_value  #=> "Value 1"
p SampleEnum.value_of(:VALUE_1).get_value  #=> "Value 1"
p SampleEnum.const_get('VALUE_1').get_value  #=> "Value 1"

#emulates Java Enum's values
SampleEnum.values.each do |m|
  p m.ordinal, m.name, m.get_value, m.to_s
end

#an Enumerable
p SampleEnum.map{|m| m.get_value} #=> ["Value 1","Value 2"]

通過擴展EnumModuleHash常量的內容(鍵和值)成為Singleton方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM