在实例化时将类转换为子类

发布于 2024-09-12 13:10:06 字数 877 浏览 6 评论 0原文

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

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

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

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

I'm writing a framework for querying the Mediawiki API. I have a Page class which represents articles on the wiki, and I've also got a Category class, which is-a Page with more specific methods (like being able to count the number of members in the category. I've also got a method Page#category? which determines if an instantiated Page object is actually representative of a Mediawiki category page, by querying the API to determine the namespace of the article.

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

What I would like to do is be able to detect if the user of my framework tries to instantiate a Mediawiki category using a Page object (ie. Page.new("Category:My Category")), and if so, instantiate a Category object, instead of a Page object, directly from the Page constructor.

It seems to me that this should be possible because it's reminiscent of single table inheritance in Rails, but I'm not sure how to go about getting it to work.

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

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

发布评论

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

评论(3

红尘作伴 2024-09-19 13:10:07

好的,有几件事:

您无法将类 A 的实例转换A 的子类 B 的实例。至少,不会自动发生。 B 可以(通常确实)包含 A 中不存在的属性,它可以具有完全不同的构造函数等。因此,据我所知,没有任何 OO 语言将允许您“转换”就这样上课。

即使在静态类型语言中,当您实例化 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 中,您可以而且应该使用方法 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 方法来实例化相应的类。

Ok, couple of things:

You can't convert an instance of a class A to an instance of A's subclass B. At least, not automatically. B can (and usually does) contain attributes not present in A, it can have completely different constructor etc. So, AFAIK, no OO language will allow you to "convert" classes that way.

Even in static-typed languages, when you instantiate B, and then assign it to a variable a of type A, it is still instance of B, it is not converted to its ancestor class whatsoever.

Ruby is a dynamic language with powerful reflection capabilities, so you can always decide which class to instantiate in the runtime - check this out:

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

So, no need for any conversion here - just instantiate the class you need in the first place.

Another thing: as I mentioned in the comment, method category? is simply wrong, as it violates OOP principles. In Ruby, you can - and should - use method is_a?, so your check will look like:

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

This is just a tip of the iceberg, there's lot more about instantiating different classes, and another question I have linked in the comment can be a great starting point, although some code examples there might confuse you. But it is definitely worth understanding them.

Edit: After re-reading your updated question, it seems to me that the right way for you would be to create a factory class and let it do the detecting and instantiating different page types. So, user wouldn't call Page.new directly, but rather call something like

MediaWikiClient.get_page "Category:My Category"

and get_page method would instantiate corresponding class.

屋檐 2024-09-19 13:10:07

为什么不做这样的事情呢?能够做到这一点,就已经是一个足够好的理由了!

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

Why not something like this? Being able to do that is a good enough reason to do it!

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
辞别 2024-09-19 13:10:07

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

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

You could define a method that instantiate the class and returns the instance.
This is know as Factory Pattern

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

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