2016-10-20 43 views
2

一旦我有一個File實例,我想檢查是否它的文件格式和extend該實例與相應的方法一致:紅寶石:文件格式通過模塊擴展文件處理

module MP3 
    def self.extended(base) 
    raise "Can only extend a File" unless base.is_a?(File) 
    raise "Incorrect file format" unless is_mp3?(base) 
    end 

    def self.is_mp3?(file) 
    # Full metadata check if it is a MP3 format 
    end 

    def year 
    # Extract year from metadata 
    end 
end 

song = File.new("song.mp3") 
if MP3.is_mp3?(song) 
    song.extend(MP3) 
    puts song.ctime # Original File method 
    puts song.year # Extended MP3 method 
end 

picture = File.new("picture.jpg") 
MP3.is_mp3?(picture) #=> False 
picture.extend(MP3) #=> raise "Incorrect file format" 

我猜是不是傳統的,但我的需求是:

  • 能夠處理多種文件格式。
  • 在知道其格式之前打開文件。
  • 重新使用相同的File實例,而不必創建新對象。 (見下文)
  • 在同一個對象中同時使用原始的File方法和格式特定的方法。
  • 添加相應方法前檢查文件格式是否正確。

該方法是否正確?

這個問題是後續的previous question

我想延長的,而不是因爲我使用File的包裝,保存在RAM中的整個文件中創建一個新的實例現有File實例(從磁帶驅動器不允許順序訪問讀取)。

回答

2

你提出的建議將選擇使用哪個類的邏輯放在調用者的代碼中。每次添加新文件類型時,都需要在使用代碼的任何地方進行更改。使用Factory pattern。寫一個類(工廠),它檢查文件名並決定要做什麼。除了我要使用更優越的Pathname

require "pathname" 

class Pathname::Format 
    def self.from_filename(filename) 
    path = Pathname.new(filename) 

    from_pathname!(path) 

    return path 
    end 

    def self.from_pathname!(path) 
    case path.extname 
    when ".mp3" 
     path.extend(MP3) 
    when ".jpg" 
     path.extend(JPG) 
    end 

    return 
    end 
end 

要點是將該決定放入工廠類,而不是在調用代碼中。

然後你可以編寫你的模塊。

module JPG 
    def type 
    return "JPG" 
    end 
end 

module MP3 
    def type 
    return "MP3" 
    end 

    def year 
    puts "MP3 year called" 
    end 
end 

現在調用者只是使用工廠。

# From a filename 
song = Pathname::Format.from_filename("song.mp3") 
puts song.ctime # Original File method 
puts song.year # Extended MP3 method 

# From a Pathname 
picture = Pathname.new("picture.jpg") 
Pathname::Format.from_pathname!(picture) 
puts picture.type 

代替具有的特殊的方法增殖,以檢查是否對象是特定類型的,無論是檢查type方法,檢查是否這是一個樣的模塊的,或依靠duck typing的。

if song.type == "MP3" 
    puts song.year 
end 

if song.kind_of?(MP3) 
    puts song.year 
end 

if song.respond_to?("year") 
    puts song.year 
end 
+0

正如我所說,在我的情況下,文件是打開之前,我不想創建一個新的實例。這就是爲什麼我正在考慮擴展實例,而不是課堂。基本上,這是因爲我需要傳遞比'filename'更多的東西,使用大量內存。所以這種直接從'filename'創建實例的方法對我不起作用。 – Victor

+0

@Victor關鍵是將所有的邏輯放到工廠類中。我重新激活了工廠來證明這一點。 – Schwern

+0

@Victor讓工廠方法的班級爲您開放。你是什​​麼意思「更多的記憶」?任意擴展對象比使用特定子類的庫存類實例在性能方面更受懲罰。 – tadman