2010-07-30 81 views
3

我正在編寫一個查詢Mediawiki API的框架。我有一個Page類,代表維基上的文章,我也有Category類,其中是-aPage更具體的方法(如能夠計算類別中的成員數量。也有一個方法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 

我想什麼做的是能夠檢測我的框架的用戶是否嘗試使用Page對象(例如Page.new("Category:My Category"))實例化Mediawiki類別,如果是,則實例化Category對象,而不是Page對象,直接來自Page構造函數。

在我看來,這應該是可能的,因爲它讓人想起Rails中的單個表繼承,但我不知道如何去實現它的工作。

+1

'Page'中的'category?'方法意味着祖先類意識到了子類,這看起來不是好主意,OOP明智。另外,你能澄清你的最後一句話嗎? – 2010-07-30 17:51:06

+0

@Mladen:對Ruby語法不太確定,但他似乎希望'Category foo = new Page()'而不是'Page bar = new Category()'。 – 2010-07-30 17:56:01

+3

然後他應該看看http://stackoverflow.com/questions/746207/ruby-design-pattern-how-to-make-an-extensible-factory-class,一般來說,谷歌有一些關於工廠模式。 – 2010-07-30 18:02:13

回答

6

好吧,兩件事情:

不能轉換實例類AA的子類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?是完全錯誤的,因爲它違反了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方法將實例化相應的類。

+0

我試圖在我的編輯中使它更清晰,但是'Page#category?'沒有檢查對象是什麼類的類,它使用頁面的信息來確定它屬於哪個名稱空間。我不知道我還會把那個放在哪裏...... – 2010-07-31 14:25:24

+0

不管怎樣,我想我對Factory模式並不是很熟悉,但是這似乎是要走的路,謝謝。 – 2010-07-31 14:27:13

+0

啊,對不起,我沒有意識到你在談論WikiMedia命名空間,而不是Ruby命名空間! :)無論如何,檢查WM命名空間並相應地實例化適當的類的地方將是工廠。當然,您可以將工廠方法放入您的「Page」類,再次查看另一個問題 - Brian Campbell在那裏給出了很好的答案和代碼示例。 – 2010-07-31 14:40:34

1

您可以定義一個實例化類並返回實例的方法。 這是知道的Factory Pattern


class PageFactory 
    def create() // the pattern uses "create".. but "new" is more Ruby' style 
    if (namespace == CATEGORY_NAMESPACE) 
     return Category.new 
    else 
     return Page.new 
    end 
    end 
end 

class ClientClass 
    def do_something() 
    factory = PageFactory.new 
    my_page = factory.create() 
    my_page.do_someting() 
    end 
end 

+0

這不行。 'PageFactory'中的'new'方法是一個實例方法,並且您正在'doSomething'中調用類方法。另外,重新定義'new'方法並不是個好主意,因爲它用於實例化已被調用的類。順便說一句,Ruby約定是使用underscore_separated方法名稱,而不是camelCased。 – 2010-07-31 07:27:35

1

爲什麼不是這樣的?能夠做到這一點是一個足夠的理由來做到這一點!

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