Ruby中抽象类的替代方法?

时间:2023-01-19 19:15:07

I am new to Ruby. A simple example, what I need:

我是Ruby的新手。一个简单的例子,我需要的:

class Animal
   abstract eat()

class Cat < Animal
   eat():
     implementation

class Dog < Animal
   eat():
     implementation

In other words, the eat() method should be required for all the classes which extend Animal.

换句话说,所有扩展Animal的类都需要eat()方法。

In JAVA I would just use an abstract class, but after doing some research I found that many people don't use it in Ruby and mixin / modules are recommended instead.

在JAVA中,我只使用一个抽象类,但经过一些研究后我发现许多人不在Ruby中使用它,而建议使用mixin / modules。

However, I don't understand, if modules can do more than just include an addition methods. To be exact, can modules set the requirements for classes which methods they must implement (if yes, an example would be appreciated)?

但是,我不明白,如果模块可以做的不仅仅是包含一个额外的方法。确切地说,模块是否可以为类必须实现哪些方法设置要求(如果是,可以赞赏一个例子)?

To sum up, what should I use in this case, when I want to be sure, that all classes of the same type have particular methods and implement them in their own way?

总而言之,在这种情况下我应该使用什么,当我想确定,所有相同类型的类都有特定的方法并以自己的方式实现它们?

5 个解决方案

#1


9  

Ruby does not offer this functionality, no. You are responsible for making sure that your classes implement what they ought to implement.

Ruby不提供此功能,不。您有责任确保您的类实现他们应该实现的内容。

Part of the reason that such functionality is impossible for Ruby is that Ruby classes can be reopened, and Ruby supports loading arbitrary code at runtime, so we can't know whether a class implements a certain interface until we try to call it.

Ruby不可能实现这样的功能的部分原因是Ruby类可以重新打开,Ruby支持在运行时加载任意代码,因此在我们尝试调用它之前,我们无法知道类是否实现了某个接口。

Supposing an Animal must have an eat method, and I do the following:

假设动物必须有吃法,我会做以下事情:

class Cat < Animal
  def talk
    puts "meow"
  end
end

class Cat
  def eat
    puts "om nom nom"
  end
end

By the end of that file, the Cat will have its eat definition, because Ruby classes can reopened and modified multiple times. Should the code error out after the first definition because eat wasn't defined yet? That implementation would hurt more than it would help, since reopening classes is common, even if this example is contrived. Should it error out once the eat method is called and does not exist, so we can be certain that it's defined once we need it? Well, if the method were missing, that would happen, anyway. The interpreter can never know if another class definition is on the way, so it can never cut you off until the method is actually called.

在该文件的末尾,Cat将具有其eat定义,因为Ruby类可以多次重新打开和修改。在第一个定义之后代码是否会出错,因为还没有定义吃?由于重新开放课程很常见,即使这个例子是人为的,这种实施方式的伤害也会超过它的帮助。如果调用eat方法并且不存在,它是否应该出错,那么我们可以确定它是在我们需要它时定义的吗?好吧,如果方法丢失了,那么无论如何都会发生。解释器永远不会知道另一个类定义是否正在进行中,因此在实际调用该方法之前它永远无法切断。

In short, superclasses simply cannot possibly require a method to be defined in Ruby, because the dynamic nature of classes contradict such a goal.

简而言之,超类根本不可能要求在Ruby中定义方法,因为类的动态性质与这样的目标相矛盾。

Sorry! This is a place where unit testing might come in handy, though, to ensure that your subclasses do what they're supposed to be doing, anyway.

抱歉!不过,这是一个单元测试可能会派上用场的地方,以确保您的子类能够完成他们应该做的事情。

#2


19  

Use a module which defines the methods that must be implemented.

使用定义必须实现的方法的模块。

module Animal
  def eat
    raise NotImplementedError
  end
end

class Cat
  include Animal

  def eat
    "Om nom nom"
  end
end

class Dog
  include Animal
end

c = Cat.new
c.eat # => "Om nom nom"

d = Dog.new
d.eat # => NotImplementedError

#3


2  

Yo can just use empty methods, or make them raise errors to force subclasses to implement them.

Yo可以使用空方法,或者让它们引发错误以强制子类实现它们。

class Base
  def abstract_method
  end

  def mandatory_abstract_method
    raise NotImplementedError.new("You must implement this")
  end
end

The down side is this will only enforce it when the method is actually called. Ruby is a dynamic language and it doesn't have any compile-time checking.

缺点是这只会在实际调用方法时强制执行。 Ruby是一种动态语言,它没有任何编译时检查。

#4


1  

There's no equivalent to abstract classes in Ruby. Mostly this is because Ruby is dynamically typed, which means that the concept of abstract classes doesn't really apply.

Ruby中没有与抽象类相同的东西。这主要是因为Ruby是动态类型的,这意味着抽象类的概念并不真正适用。

#5


0  

If you really want to implement abstraction then you use abstraction gem.

如果你真的想要实现抽象,那么你使用抽象gem。

sudo gem install abstraction

Referencing example Example from Abstraction gem WIKI.

引用来自Abstraction gem WIKI的示例示例。

class Car
  abstract

  def go_forward
  # ...
 end
end

Car.new
> AbstractClassError: Car is an abstract class and cannot be instantiated

class Convertible < Car
  def door_count
    2
  end
end

class Sedan < Car
  def door_count
    4
  end
end

Convertible.new  # => #<Convertible:0x8fdf4>

#1


9  

Ruby does not offer this functionality, no. You are responsible for making sure that your classes implement what they ought to implement.

Ruby不提供此功能,不。您有责任确保您的类实现他们应该实现的内容。

Part of the reason that such functionality is impossible for Ruby is that Ruby classes can be reopened, and Ruby supports loading arbitrary code at runtime, so we can't know whether a class implements a certain interface until we try to call it.

Ruby不可能实现这样的功能的部分原因是Ruby类可以重新打开,Ruby支持在运行时加载任意代码,因此在我们尝试调用它之前,我们无法知道类是否实现了某个接口。

Supposing an Animal must have an eat method, and I do the following:

假设动物必须有吃法,我会做以下事情:

class Cat < Animal
  def talk
    puts "meow"
  end
end

class Cat
  def eat
    puts "om nom nom"
  end
end

By the end of that file, the Cat will have its eat definition, because Ruby classes can reopened and modified multiple times. Should the code error out after the first definition because eat wasn't defined yet? That implementation would hurt more than it would help, since reopening classes is common, even if this example is contrived. Should it error out once the eat method is called and does not exist, so we can be certain that it's defined once we need it? Well, if the method were missing, that would happen, anyway. The interpreter can never know if another class definition is on the way, so it can never cut you off until the method is actually called.

在该文件的末尾,Cat将具有其eat定义,因为Ruby类可以多次重新打开和修改。在第一个定义之后代码是否会出错,因为还没有定义吃?由于重新开放课程很常见,即使这个例子是人为的,这种实施方式的伤害也会超过它的帮助。如果调用eat方法并且不存在,它是否应该出错,那么我们可以确定它是在我们需要它时定义的吗?好吧,如果方法丢失了,那么无论如何都会发生。解释器永远不会知道另一个类定义是否正在进行中,因此在实际调用该方法之前它永远无法切断。

In short, superclasses simply cannot possibly require a method to be defined in Ruby, because the dynamic nature of classes contradict such a goal.

简而言之,超类根本不可能要求在Ruby中定义方法,因为类的动态性质与这样的目标相矛盾。

Sorry! This is a place where unit testing might come in handy, though, to ensure that your subclasses do what they're supposed to be doing, anyway.

抱歉!不过,这是一个单元测试可能会派上用场的地方,以确保您的子类能够完成他们应该做的事情。

#2


19  

Use a module which defines the methods that must be implemented.

使用定义必须实现的方法的模块。

module Animal
  def eat
    raise NotImplementedError
  end
end

class Cat
  include Animal

  def eat
    "Om nom nom"
  end
end

class Dog
  include Animal
end

c = Cat.new
c.eat # => "Om nom nom"

d = Dog.new
d.eat # => NotImplementedError

#3


2  

Yo can just use empty methods, or make them raise errors to force subclasses to implement them.

Yo可以使用空方法,或者让它们引发错误以强制子类实现它们。

class Base
  def abstract_method
  end

  def mandatory_abstract_method
    raise NotImplementedError.new("You must implement this")
  end
end

The down side is this will only enforce it when the method is actually called. Ruby is a dynamic language and it doesn't have any compile-time checking.

缺点是这只会在实际调用方法时强制执行。 Ruby是一种动态语言,它没有任何编译时检查。

#4


1  

There's no equivalent to abstract classes in Ruby. Mostly this is because Ruby is dynamically typed, which means that the concept of abstract classes doesn't really apply.

Ruby中没有与抽象类相同的东西。这主要是因为Ruby是动态类型的,这意味着抽象类的概念并不真正适用。

#5


0  

If you really want to implement abstraction then you use abstraction gem.

如果你真的想要实现抽象,那么你使用抽象gem。

sudo gem install abstraction

Referencing example Example from Abstraction gem WIKI.

引用来自Abstraction gem WIKI的示例示例。

class Car
  abstract

  def go_forward
  # ...
 end
end

Car.new
> AbstractClassError: Car is an abstract class and cannot be instantiated

class Convertible < Car
  def door_count
    2
  end
end

class Sedan < Car
  def door_count
    4
  end
end

Convertible.new  # => #<Convertible:0x8fdf4>