• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >ruby > Ruby设计模式编程之适配器模式实战攻略

Ruby设计模式编程之适配器模式实战攻略

作者: 字体:[增加 减小] 来源:互联网

通过本文主要向大家介绍了d ruby洋服设计,ruby编程语言,ruby编程语言 pdf,ruby元编程,ruby编程等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

适配器模式
适配器模式可以用于对不同的接口进行包装以及提供统一的接口,或者是让某一个对象看起来像是另一个类型的对象。在静态类型的编程语言里,我们经常使用它去满足类型系统的特点,但是在类似Ruby这样的弱类型编程语言里,我们并不需要这么做。尽管如此,它对于我们来说还是有很多意义的。
当使用第三方类或者库的时候,我们经常从这个例子开始(start out fine):

def find_nearest_restaurant(locator)
 locator.nearest(:restaurant, self.lat, self.lon)
end
</div>

我们假设有一个针对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
</div>

这是一个比较务实的解决方案。或许我们也不再需要考虑去支持另一个库了。也或许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)

</div>

特意假设的例子就到此为止,接下来让我们看看真实的代码。

实例
今天一大早,你的leader就匆匆忙忙跑过来找到你:“快,快,紧急任务!最近ChinaJoy马上就要开始了,老板要求提供一种直观的方式,可以查看到我们新上线的游戏中每个服的在线人数。”
你看了看日期,不是吧!这哪里是马上要开始了,分明是已经开始了!这怎么可能来得及呢?
“没关系的。”你的leader安慰你道:“功能其实很简单的,接口都已经提供好了,你只需要调用一下就行了。”
好吧,你勉为其难地接受了,对于这种突如其来的新需求,你早已习惯。
你的leader向你具体描述了一下需求,你们的游戏目前有三个服,一服已经开放一段时间了,二服和三服都是新开的服。设计的接口非常轻便,你只需要调用Utility.online_player_count(Fixnum),传入每个服对应的数值就可以获取到相应服在线玩家的数量了,如一服传入1,二服传入2,三服则传入3。如果你传入了一个不存在的服,则会返回-1。然后你只要将得到的数据拼装成XML就好,具体的显示功能由你的leader来完成。
好吧,听起来功能并不是很复杂,如果现在就开始动工好像还来得及,于是你马上敲起了代码。
首先定义一个用于统计在线人数的父类PlayerCount,代码如下:

class PlayerCount 
 
  def server_name 
    raise "You should override this method in subclass." 
  end 
   
  def player_count 
    raise "You should override this method in subclass." 
  end 
 
end 
</div>

接着定义三个统计类继承PlayerCount,分别对应了三个不同的服,如下所示:

class ServerOne < PlayerCount 
 
  def server_name 
    "一服" 
  end 
   
  def player_count 
    Utility.online_player_count(1) 
  end 
 
end 

class ServerTwo < PlayerCount 
 
  def server_name 
    "二服" 
  end 
   
  def player_count 
    Utility.online_player_count(2) 
  end 
 
end 

class ServerThree < PlayerCount 
 
  def server_name 
    "三服" 
  end 
   
  def player_count 
    Utility.online_player_count(3) 
  end 
 
end 

</div>

然后定义一个XMLBuilder类,用于将各服的数据封装成XML格式,代码如下:

class XMLBuilder 
 
  def self.build_xml player 
    builder = "" 
    builder << "<root>" 
    builder << "<server>" << player.server_name << "</server>" 
    builder << "<player_count>" << player.player_count.to_s << "</player_count>" 
    builder << "</root>" 
  end 
 
end 
</div>

这样的话,所有代码就完工了,如果你想查看一服在线玩家数只需要调用:

XMLBuilder.build_xml(ServerOne.new) 
</div>

查看二服在线玩家数只需要调用:

XMLBuilder.build_xml(ServerTwo.new) 
</div>

查看三服在线玩家数只需要调用:

XMLBuilder.build_xml(ServerThree.new) 
</div>

咦?你发现查看一服在线玩家数的时候,返回值永远是-1,查看二服和三服都很正常。
你只好把你的leader叫了过来:“我感觉我写的代码没有问题,但是查询一服在线玩家数总是返回-1,为什么会这样呢?”
“哎呀!”你的leader猛然想起,“这是我的问题,前面没跟你解释清楚。由于我们的一服已经开放一段时间了,查询在线玩家数量的功能早就有了,使用的是ServerFirst这个类。当时写Utility.online_player_count()这个方法主要是为了针对新开的二服和三服,就没把一服的查询功能再重复做一遍。这种情况下可以使用适配器模式,这个模式就是为了解决接口之间不兼容的问题而出现的。”
其实适配器模式的使用非常简单,核心思想就是只要能让两个互不兼容的接口能正常对接就行了。上面的代码中,XMLBuilder中使用PlayerCount来拼装XML,而ServerFirst并没有继承PlayerCount,这个时候就需要一个适配器类来为XMLBuilder和ServerFirst之间搭起一座桥梁,毫无疑问,ServerOne就将充当适配器类的角色。修改ServerOne的代码,如下所示:

class ServerOne < PlayerCount 
 
  def initialize 
    @serverFirst = ServerFirst.new 
  end 
 
  def server_name 
    "一服" 
  end 
   
  def player_count 
    @serverFirst.online_player_count 
  end 
 
end 
</div>

 
这样通过ServerOne的适配,XMLBuilder和ServerFirst之间就成功完成对接了!使用的时候我们甚至无需知道有ServerFirst这个类,只需要正常创建ServerOne的实例就行了。
需要值得注意的一点是,适配器模式不并是那种会让架构变得更合理的模式,更多的时候它只是充当救火队员的角色,帮助解决由于前期架构设计不合理导致的接口不匹配的问题。更好的做法是在设计的时候就尽量把以后可能出现的情况多考虑一些,在这个问题上不要向你的leader学习。

MultiJSON
ActiveSupport在做JSON格式的解码时,用到的是MultiJSON,这是一个针对JSON库的适配器。每一个库都能够解析JSON,但是做法却不尽相同。让我们分别看看针对oj和yajl的适配器。 (提示: 可在命令行中输入qw multi_json查看源码。)

module MultiJson
 module Adapters
  class Oj < Adapter
   #...
   def load(string, options={})
    options[:symbol_keys] = options.delete(:symbolize_keys)
    ::Oj.load(string, options)
   end
   #...
</div>

Oj的适配器修改了options哈希表,使用Hash#delete将:symbolize_keys项转换为Oj的:symbol_keys项:

options = {:symbolize_keys => true}
options[:symbol_keys] = options.delete(:symbolize_keys) # => true
options                         # => {:symbol_keys=>true}
</div>

接下来MultiJSON调用了::Oj.load(string, options)。MultiJSON适配后的API跟Oj原有的API非常相似,在此不必赘述。不过你是否注意到,Oj是如何引用的呢?::Oj引用了顶层的Oj类,而不是MultiJson::Adapters::Oj。
现在让我们看看MultiJSON又是如何适配Yajl库的:

module MultiJson
 module Adapters
  class Yajl < Adapter
   #...
   def load(string, options={})
    ::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(strin
  


 

您可能想查找下面的文章:

  • Ruby设计模式编程中对外观模式的应用实例分析
  • Ruby设计模式编程中使用Builder建造者模式的实例
  • Ruby设计模式编程之适配器模式实战攻略
  • Ruby使用设计模式中的代理模式与装饰模式的代码实例

相关文章

  • Ruby on Rails中MVC结构的数据传递解析
  • Ruby简明教程之数组和Hash介绍
  • Ruby元编程小结
  • 提升Ruby on Rails性能的几个解决方案
  • CentOS 7下配置Ruby语言开发环境的方法教程
  • Ruby教程之注释、变量声明以及数组操作
  • 源代码快速定位工具-qwandry使用指南
  • Ruby学习笔记一
  • 详解Ruby on Rails中的mailer相关使用
  • 深入理解Ruby on Rails中的缓存机制

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • ruby 杂项
    • 在Docker中自动化部署Ruby on Rails的教程
    • Ruby使用REXML库来解析xml格式数据的方法
    • 在Ruby中利用Net::SMTP类发送电子邮件的教程
    • Ruby中的类Google Map/Reduce框架Skynet介绍
    • 使用Ruby编写发送邮件的程序的简单教程
    • Ruby简洁学习笔记(一):字符串、数字、类和对象
    • Monkey Patch猴子补丁编程方式及其在Ruby中的运用
    • 用实际代码演示Ruby的容易被误解的6个特性
    • 在 Ubuntu 12.04 Server 上安装部署 Ruby on Rails 应用

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有