Преобразовать класс в подкласс при создании - PullRequest
3 голосов
/ 30 июля 2010

Я пишу фреймворк для запроса Mediawiki API .У меня есть класс Page, который представляет статьи в вики, и у меня также есть класс Category, который is-a 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, непосредственно из конструктора Page.

Мне кажется, что это должно быть возможно, потому что это напоминает наследование одной таблицы в Rails, но я не уверен, какзаставить его работать.

Ответы [ 3 ]

6 голосов
/ 31 июля 2010

Хорошо, пара вещей:

Вы не можете преобразовать экземпляр класса A в экземпляр подкласса A B. По крайней мере, не автоматически. B может (и обычно содержит) атрибуты, отсутствующие в A, у него может быть совершенно другой конструктор и т. Д. Итак, AFAIK, никакой язык 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? просто неправильный, поскольку он нарушает принципы ООП. В 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 будут создавать соответствующий класс.

2 голосов
/ 28 апреля 2012

Почему бы не что-то подобное?Быть способным сделать это - достаточно веская причина для этого!

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
1 голос
/ 31 июля 2010

Вы можете определить метод, который создает экземпляр класса и возвращает экземпляр. Это известно как Заводской шаблон

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...