2016-10-20 46 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