Ruby 中的抽象方法

发布于 2024-11-26 01:34:37 字数 84 浏览 1 评论 0原文

如何强制子类实现 Ruby 中的方法。 Ruby 中似乎没有抽象关键字,而我在 Java 中则采用这种方法。还有另一种更像 Ruby 的方式来强制抽象吗?

How can I force a subclass to implement a method in Ruby. There doesn't seem to be an abstract keyword in Ruby, which is the approach I would take in Java. Is there another more Ruby-like way to enforce abstract?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

温柔一刀 2024-12-03 01:34:37

抽象方法在 Ruby 中应该不太有用,因为它不是静态类型。

然而,这就是我所做的:

class AbstractThing
  MESS = "SYSTEM ERROR: method missing"

  def method_one; raise MESS; end
  def method_two; raise MESS; end
end

class ConcreteThing < AbstractThing
  def method_one
     puts "hi"
  end
end

a = ConcreteThing.new
a.method_two # -> raises error.

然而,这似乎很少是必要的。

Abstract methods are supposed to be less useful in Ruby because it's not strongly statically typed.

However, this is what I do:

class AbstractThing
  MESS = "SYSTEM ERROR: method missing"

  def method_one; raise MESS; end
  def method_two; raise MESS; end
end

class ConcreteThing < AbstractThing
  def method_one
     puts "hi"
  end
end

a = ConcreteThing.new
a.method_two # -> raises error.

It rarely seems to be necessary, however.

拥有 2024-12-03 01:34:37

我喜欢 pvandenberk 的答案,但我会对其进行如下改进:

module Canine      # in Ruby, abstract classes are known as modules
  def bark
    fail NotImplementedError, "A canine class must be able to #bark!"
  end
end

现在,如果您创建一个属于 Canine “抽象类”的类(即具有 Canine 的类)模块),如果发现 #bark 方法未实现,它会抱怨:

class Dog
  include Canine   # make dog belong to Canine "abstract class"
end

Dog.new.bark       # complains about #bark not being implemented

class Dog
  def bark; "Bow wow!" end
end

# Now it's OK:
Dog.new.bark #=> "Bow wow!"

请注意,由于 Ruby 类不是静态的,而是始终开放更改,Dog 类本身不能强制存在#bark 方法,因为它不知道什么时候应该完成。如果你作为程序员这样做,那么这个时候就由你来测试它。

I like the answer by pvandenberk, but I would improve it as follows:

module Canine      # in Ruby, abstract classes are known as modules
  def bark
    fail NotImplementedError, "A canine class must be able to #bark!"
  end
end

Now if you make a class belonging to Canine "abstract class" (ie. a class that has Canine module in its ancestors), it will complain if it is found that #bark method is not implemented:

class Dog
  include Canine   # make dog belong to Canine "abstract class"
end

Dog.new.bark       # complains about #bark not being implemented

class Dog
  def bark; "Bow wow!" end
end

# Now it's OK:
Dog.new.bark #=> "Bow wow!"

Note that since Ruby classes are not static, but always open to changes, Dog class itself cannot enforce existence of #bark methods, since it doesn't know when is it supposed to be finished. If you as a programmer do, it is up to you to test it at such time.

月下凄凉 2024-12-03 01:34:37

我喜欢的方法类似,但略有不同...我更喜欢如下,因为它使代码自我记录,为您提供与 Smalltalk 非常相似的东西:

class AbstractThing
  def method_one; raise "SubclassResponsibility" ; end
  def method_two; raise "SubclassResponsibility" ; end
  def non_abstract_method; method_one || method_two ; end
end

有些人会抱怨这不太 DRY,并坚持创建异常子类和/或将 "SubclassResponsibility" 字符串放入常量中,但恕我直言 你可以把东西弄干到被摩擦的程度,这通常不是一件好事。例如,如果您的代码库中有多个抽象类,那么您将在哪里定义 MESS 字符串常量?!?

My preferred approach is similar but slightly different... I prefer it as follows, because it makes the code self-documenting, giving you something very similar to Smalltalk:

class AbstractThing
  def method_one; raise "SubclassResponsibility" ; end
  def method_two; raise "SubclassResponsibility" ; end
  def non_abstract_method; method_one || method_two ; end
end

Some people will complain that this is less DRY, and insist on creating an exception subclass and/or put the "SubclassResponsibility" string in a constant, but IMHO you can dry things up to the point of being chafed, and that is not usually a good thing. E.g. if you have multiple abstract classes across your code base, where would you define the MESS string constant?!?

弥枳 2024-12-03 01:34:37

我喜欢使用像 abstract_method 这样的 gem,它提供了 dsl Rails 风格的语法抽象方法:

class AbstractClass
  abstract_method :foo
end

class AbstractModule
  abstract_method :bar
end

class ConcreteClass < AbstractClass
  def foo
    42
  end
end

I like the use of a gem like abstract_method which gives a dsl rails style syntax abstract methods:

class AbstractClass
  abstract_method :foo
end

class AbstractModule
  abstract_method :bar
end

class ConcreteClass < AbstractClass
  def foo
    42
  end
end
心如荒岛 2024-12-03 01:34:37

如果继承类中未定义方法“foo”、“bar”和“mate”,则此代码将不允许您加载该类。

它没有考虑到跨多个文件定义的类,但老实说,我们中的许多人实际上是否跨多个文件定义了类方法?我的意思是如果你不计算混合。 (这确实说明了这一点)

def self.abstract(*methods_array)
  @@must_abstract ||= []
  @@must_abstract = Array(methods_array)
end
def self.inherited(child)
   trace = TracePoint.new(:end) do |tp|
      if tp.self == child #modules also trace end we only care about the class end   
        trace.disable
        missing = ( Array(@@must_abstract) - child.instance_methods(false) )
        raise NotImplementedError, "#{child} must implement the following method(s) #{missing}" if missing.present?
      end
  end 
  trace.enable
end

abstract :foo
abstract :bar, :mate

This code will not let you load the class if the methods 'foo', 'bar' and 'mate' are not defined in the inherited class.

It does not account for classes being defined across many files, but lets get honest do many of us actually define class methods across many files? I mean if you don't count mix-ins. (which this does account for)

def self.abstract(*methods_array)
  @@must_abstract ||= []
  @@must_abstract = Array(methods_array)
end
def self.inherited(child)
   trace = TracePoint.new(:end) do |tp|
      if tp.self == child #modules also trace end we only care about the class end   
        trace.disable
        missing = ( Array(@@must_abstract) - child.instance_methods(false) )
        raise NotImplementedError, "#{child} must implement the following method(s) #{missing}" if missing.present?
      end
  end 
  trace.enable
end

abstract :foo
abstract :bar, :mate
幸福还没到 2024-12-03 01:34:37

如果您想在创建类的实例时抛出错误,您可以执行以下操作

class AbstractClass
  def self.new(args)
    instance = allocate # make memory space for a new object
    instance.send(:default_initialize, args)
    instance.send(:initialize, args)
    instance
  end

  #This is called whenever object created, regardless of whether 'initialize' is overridden
  def default_initialize(args)
    self.abstract_method #This will raise error upon object creation
  end
  private :default_initialize

  def initialize(args)
   # This can be overridden by new class
  end
end


class NewClass < AbstractClass
end

NewClass.new #Throw error

If you want to have an error thrown when you create an instance of the class you could do the following

class AbstractClass
  def self.new(args)
    instance = allocate # make memory space for a new object
    instance.send(:default_initialize, args)
    instance.send(:initialize, args)
    instance
  end

  #This is called whenever object created, regardless of whether 'initialize' is overridden
  def default_initialize(args)
    self.abstract_method #This will raise error upon object creation
  end
  private :default_initialize

  def initialize(args)
   # This can be overridden by new class
  end
end


class NewClass < AbstractClass
end

NewClass.new #Throw error
半世晨晓 2024-12-03 01:34:37

因为问题是(重点)“如何强制子类在 Ruby 中实现方法”,所以我认为我们可以使用 TDD :D,例如:rspec 共享示例< /code>

shared_examples "MUST implement abstract method" do |method_sym|
  it { is_expected.to respond_to(method_sym) }
end

describe Stack do
  it_behaves_like "MUST implement abstract method", :push
  it_behaves_like "MUST implement abstract method", :pop
end

也许 测试比 Abstract 更好 :D ,参考:http://morningcoffee.io/interfaces-in-ruby.html

Because the question is (focus on) "How can I force a subclass to implement a method in Ruby", so i think we can use TDD :D, for example: rspec shared example

shared_examples "MUST implement abstract method" do |method_sym|
  it { is_expected.to respond_to(method_sym) }
end

describe Stack do
  it_behaves_like "MUST implement abstract method", :push
  it_behaves_like "MUST implement abstract method", :pop
end

Maybe Tests are better than Abstract :D , reference: http://morningcoffee.io/interfaces-in-ruby.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文