3

我正在编写一个用于查询Mediawiki API的框架。我有一个Page代表 wiki 上的文章的类,我还有一个Category类,它具有 Page更具体的方法(比如能够计算类别中的成员数量。我还有一个Page#category?方法Page通过查询 API 来确定文章的命名空间,确定实例化对象是否真的代表 Mediawiki 类别页面。

class Page
  def initialize(title)
    # do initialization stuff
  end

  def category?
    # query the API to get the namespace of the page and then...
    namespace == CATEGORY_NAMESPACE
  end
end

class Category < Page
  # ...
end

我想要做的是能够检测我的框架的用户是否尝试使用 Page 对象(即。Page.new("Category:My Category"))实例化 Mediawiki 类别,如果是,则直接从构造函数实例化一个Category对象,而不是一个对象。PagePage

在我看来,这应该是可能的,因为它让人想起 Rails 中的单表继承,但我不确定如何让它工作。

4

3 回答 3

6

好的,有几件事:

您不能类的实例转换为子类的A实例。至少,不是自动的。可以(并且通常确实)包含 中不存在的属性,它可以具有完全不同的构造函数等。因此,AFAIK,没有 OO 语言允许您以这种方式“转换”类。ABBA

即使在静态类型语言中,当您实例化B,然后将其分配给a类型的变量时A,它仍然是 的实例B,它不会被转换为它的祖先类。

Ruby 是一种具有强大反射功能的动态语言,因此您始终可以决定在运行时实例化哪个类 - 看看这个:

puts "Which class to instantiate: "
class_name = gets.chomp
klass = Module.const_get class_name
instance = klass.new

所以,这里不需要任何转换 - 只需首先实例化您需要的类。

另一件事:正如我在评论中提到的,方法category?是完全错误的,因为它违反了 OOP 原则。在 Ruby 中,您可以并且应该使用 method is_a?,因此您的检查将如下所示:

if instance.is_a? Category
  puts 'Yes, yes, it is a category!'
else
  puts "Nope, it's something else."
end

这只是冰山一角,还有更多关于实例化不同类的内容,我在评论中链接的另一个问题可能是一个很好的起点,尽管那里的一些代码示例可能会让您感到困惑。但绝对值得了解它们。

编辑:重新阅读您更新的问题后,在我看来,正确的方法是创建一个工厂类并让它检测和实例化不同的页面类型。因此,用户不会Page.new直接调用,而是调用类似

MediaWikiClient.get_page "Category:My Category"

get_page方法将实例化相应的类。

于 2010-07-31T07:24:36.467 回答
3

为什么不这样呢?能够做到这一点是一个足够好的理由去做!

class Page
  def self.new(title)
    if self == Page and is_category?(title)
      Category.new(title)
    else
      super
    end
  end

  def self.is_category?(title)
    # ... (query the API etc.)
  end

  def initialize(title)
    # do initialization stuff
  end

  def category?
    # query the API to get the namespace of the page and then...
    namespace == CATEGORY_NAMESPACE
  end
end

class Category < Page
  # ...
end
于 2012-04-28T00:39:02.187 回答
1

您可以定义一个实例化类并返回实例的方法。这被称为工厂模式

class PageFactory
  def create(title) # the pattern uses "create".. but "new" is more Ruby' style
    namespace = title[/\A[^:]+(?=:)/]
     # matches the prefix, up to and excluding the first colon.
    if namespace == CATEGORY_NAMESPACE
      Category.new(title)
    else
      Page.new(title)
    end
  end
end

class ClientClass
  def do_something()
    factory = PageFactory.new
    my_page = factory.create('Category:Foo') 
    my_page.do_something()
  end
end

于 2010-07-31T04:57:09.853 回答