Ruby:通过模块扩展文件来处理文件格式

Ruby:通过模块扩展文件来处理文件格式,ruby,Ruby,一旦我有了一个实例,我想检查它是否与文件格式匹配,以及该实例是否与相应的方法匹配: 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

一旦我有了一个实例,我想检查它是否与文件格式匹配,以及该实例是否与相应的方法匹配:

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"
我想这不是传统,但我的需求是:

  • 能够处理多种文件格式
  • 在知道文件格式之前打开文件
  • 重复使用相同的
    文件
    实例,而无需创建新对象。(见下文)
  • 在同一对象中同时具有原始
    文件
    方法和特定于格式的方法
  • 在添加相应的方法之前,请检查文件格式是否正确
这种方法正确吗

这个问题是一个问题的后续


我想扩展现有的
文件
实例,而不是创建新实例,因为我使用的是
文件
的包装器,它将整个文件保存在RAM中(从不允许顺序访问的磁带机读取).

您的建议在调用方的代码中加入了太多选择使用哪个类的逻辑。每次添加新文件类型时,都需要在使用代码的任何地方进行更改

取而代之的是,使用一个。编写一个类(工厂),它检查文件名并决定执行什么操作。除非我要用更高级的

关键是将该决策放入工厂类,而不是调用代码

然后就可以编写模块了

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
与其使用大量的特殊方法来检查对象是否属于特定类型,不如检查
类型
方法,检查它是否是一种模块,或者依赖于


正如我所说,在我的例子中,文件是在之前打开的,我不想创建一个新实例。这就是为什么我要扩展实例,而不是类。基本上,这是因为我需要传递比
文件名
多得多的东西,占用大量内存。因此,这种直接从
文件名创建实例的方法对我不起作用。@Victor关键是将所有逻辑放入factory类中。我重新调整了工厂以演示这一点。@Victor让包含工厂方法的类为您打开这个窗口。“更多的记忆”是什么意思?任意扩展对象在性能方面比使用特定子类的stock类实例更具惩罚性。@tadman正如我所说的,文件已经打开了,我需要重用同一个实例,因为它使用GB的RAM,我不想在创建新对象的过程中移动它。我不是真的在使用MP3路径名,这就是为什么我在我的案例中指定了(非常规)要求。@Victor我们谈论的是将来将要运行的代码,不是吗?所以你可以重新构造你的应用程序来封装你已经实现的任何文件打开逻辑,只需移动代码即可。如果您真的被卡住了,这将是非常不寻常的,那么让factory方法环绕现有的File对象。这比将垃圾随机塞进文件实例要好,拥有自己的子类可以让您有权添加任何内容,而无需担心与文件自身内部的冲突。
# 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
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