代码之家  ›  专栏  ›  技术社区  ›  Daniel Vandersluis

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

  •  3
  • Daniel Vandersluis  · 技术社区  · 14 年前

    我正在编写一个框架来查询 Mediawiki API . 我有一个 Page 类,它表示wiki上的文章,我还拥有 Category 类,哪个 IS-A 使用更具体的方法(例如能够计算类别中的成员数)。我也有办法 Page#category? 它确定 对象实际上是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.new("Category:My Category") ,如果是,则实例化 类别 对象,而不是 对象,直接从 构造函数。

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

    3 回复  |  直到 6 年前
        1
  •  6
  •   Mladen Jablanović    14 年前

    好吧,有两件事:

    你不能 转换 类的实例 A 到的一个实例 的子类 B . 至少,不是自动的。 可以(通常也可以)包含不存在于 它可以有完全不同的构造器等,因此,afaik,no oo语言将允许您以这种方式“转换”类。

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

    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 方法将实例化相应的类。

        2
  •  2
  •   rewritten    12 年前

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

    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
    
        3
  •  1
  •   Eridal    6 年前

    可以定义一个实例化类并返回实例的方法。 这是众所周知的 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