今天我们暂时先放下具体的代码片段,我们将要对Rails中所实现的一个比较常见的设计模式进行一番探索,这个模式就是适配器模式(Adapter Pattern)。从一定的意义上来说,这次的探索并不全面,但是我希望能够突出一些实际的例子。
为了跟随本文的步骤,请使用qwandry打开相关的代码库,或者直接在Github上查看这些代码。
适配器模式
适配器模式可以用于对不同的接口进行包装以及提供统一的接口,或者是让某一个对象看起来像是另一个类型的对象。在静态类型的编程语言里,我们经常使用它去满足类型系统的特点,但是在类似Ruby这样的弱类型编程语言里,我们并不需要这么做。尽管如此,它对于我们来说还是有很多意义的。
当使用第三方类或者库的时候,我们经常从这个例子开始(start out fine):
def find_nearest_restaurant(locator)
locator.nearest(:restaurant, self.lat, self.lon)
end
我们假设有一个针对locator的接口,但是如果我们想要find_nearest_restaurant能够支持另一个库呢?这个时候我们可能就会去尝试添加新的特殊的场景的处理:
def find_nearest_restaurant(locator)
if locator.is_a? GeoFish
locator.nearest(:restaurant, self.lat, self.lon)
elsif locator.is_a? ActsAsFound
locator.find_food(:lat => self.lat, :lon => self.lon)
else
raise NotImplementedError, "#{locator.class.name} is not supported."
end
end
这是一个比较务实的解决方案。或许我们也不再需要考虑去支持另一个库了。也或许find_nearest_restaurant就是我们使用locator的唯一场景。
那假如你真的需要去支持一个新的locator,那又会是怎么样的呢?那就是你有三个特定的场景。再假如你需要实现find_nearest_hospital方法呢?这样你就需要在维护这三种特定的场景时去兼顾两个不同的地方。当你觉得这种解决方案不再可行的时候,你就需要考虑适配器模式了。
在这个例子中,我们可以为GeoFish以及ActsAsFound编写适配器,这样的话,在我们的其他代码中,我们就不需要了解我们当前正在使用的是哪个库了:
def find_nearest_hospital(locator)
locator.find :type => :hospital,
:lat => self.lat,
:lon => self.lon
end
locator = GeoFishAdapter.new(geo_fish_locator)
find_nearest_hospital(locator)
特意假设的例子就到此为止,接下来让我们看看真实的代码。
MultiJSON
ActiveSupport在做JSON格式的解码时,用到的是Multi