脱离 Rails 看 Ruby

在开始这篇文章之前,我需要澄清一些事情。首先,这不是一篇关于 Ruby on Rails 的文章。如果您希望了解 Rails,每周(甚至每小时)都有相关的文章和 blog 出现,它们都对这个令人兴奋的框架的众多特性大加推崇;请参见 参考资料 中的列表。其次,本文并不是想预言,Java 面对 Ruby on Rails 这样更好的 语言、工具和框架会衰败下去。所以本文并不涉及近来通常与 Ruby 最相关的主题。

别误会 —— 我认为 Rails 是令人难以置信的!它的功能极其强大,已经明显地改变了 Web 开发的面貌和步调。我只想指出一点:Ruby 要比 Rails 功能丰富,尤其是从 Java 开发人员的视角来看。

Rails 的专长是 Web 站点开发;但是,我自己没有构建过全新的 Web 站点。我所工作的大多数 Web 站点已经 使用 Struts、Tapestry 或其他技术构建起来了。在我利用 Ruby 时,基本上是将它作为一种与 Java 平台衔接的开发实践。所以在本文中,我将谈谈如果您主要是 Java 开发人员,那么应该如何利用 Ruby 进行开发。

掌握多种语言的好处

如果您掌握了多种语言,在与多个国家的朋友一起旅行时能够帮助他们消除语言障碍,从而获得尊重,会外语也会提升您在本国语言环境中的地位。掌握多种编程语言 也有同样的好处。与只会一种语言的人相比,掌握多种编程语言的开发人员在 IT 世界中会更自由(他们能够将技能应用于任何环境),而且他们在自己的编程母语 领域中也会更受尊重,因为他们了解源自这种母语的其他东西。您不想掌握多种语言吗?

感觉是如此不同

Ruby 的语法与 Java 语言的语法不同。首先,Ruby 没有方括号或分号,而且它使类型成为完全可选的。有人可能会说 Ruby 的语法很简洁,这正是它的意图:这种语言使开发人员可以迅速编写简洁的代码。

如果用 Java 语言和 Ruby 分别定义同一个类,通过比较,就能够看到这种简洁性。我先给出用 Java 语言编写的两个类 —— WordDefinition(就像是词典)。在图 1 所示的简单类图中,可以看出这两个类有几个关系(如果这种复杂性看起来不自然,请忍耐一下;这是有意义的!):

  • 一个 Word 可以拥有一个同义词(Word 的实例)集合。
  • 一个 Word 还拥有一个 Definition 集合。
  • 一个 Definition 拥有一个对 Word 的聚合关联。

图 1. 具有单词和定义的简单词典

用 Java 语言编写的类定义

在清单 1 中,用 Java 语言定义了 Word 类。注意,必须对 Definition 和同义词集合进行关系检验。这是因为在这个例子中,最初创建 Definition 时可以不带 Word 关系,最初定义 Word 时也可以不带 Definition


清单 1. 用 Java 语言编写的 Word 类

清单 1 中的 Word 类相当简单;它是一个 JavaBean,具有一个构造函数链,使用户能够用各种属性集创建 Word。还要注意,它的 synonymsdefinitions 属性 只读的(即没有针对它们的设置方法)。只能添加 一个 Definition 实例,或者为一个同义词添加 另一个 Word

在清单 2 中是相关的 Definition 类,这个类与 Word 类的相似之处是,它的 exampleSentences 属性没有对应的 set() 方法:


清单 2. 用 Java 语言编写的 Definition 类

用 Ruby 编写的类定义

在清单 3 中,可以看到用 Ruby 定义的这两个类。清单 3 确实 看起来很不一样,不是吗?


清单 3. 用 Ruby 编写的相同类

从清单 3 中可以看出,Ruby 的语法非常简洁。但是,不要让这种简洁性欺骗了您,这段代码中有许多内容!首先,在一个模块 中定义了两个类,模块本质上相当于 Java 语言中的 。另外,能够在一个文件中定义这些类,而 Java 语言要求用两个文件。您还会注意到,Ruby 的构造函数名为 initialize,而 Java 语言中的构造函数使用类名进行命名。





回页首


创建对象实例

在 Ruby 中创建新的对象实例的方式是不一样的。Ruby 并不使用 Java 代码中的 new ObjectInstance() 语法,Ruby 实际上支持在对象上调用 new 方法,即在内部调用 initialize 方法。在清单 4 中,可以看到如何在 Ruby 中创建一个 Word 实例和一些对应的 Definition


清单 4. 在 Ruby 中创建新的对象实例

在清单 4 中,我用 Ruby 的 require 方法(这个方法可以在 Kernel 类中找到)“导入了” dictionary 模块。然后通过 Object.new 语法创建一个新的 Word 实例(ebullient)。尽管导入了 dictionary 模块,但是仍然需要对对象实例进行限定,因此采用 Dictionary::Word。如果在 require 子句后面编写了 include Dictionary,那么也可以去掉 Dictionary:: 前缀。

默认参数值

您是否注意到了,在清单 4 中创建 happy_wrd 实例时没有 指定 Definition 或同义词集合。我只为 spellingpart_of_speech 传递了值。可以进行这样的省略是因为 Ruby 支持参数的默认值。在清单 3 定义的 Wordinitialize 方法中,指定了 definitions = []synonyms = [] 作为参数,这基本上就是告诉 Ruby 如果调用者没有提供这些集合,就将它们默认设置为空集合

还要注意,在 清单 3Definitioninitialize 方法如何通过将 example_sentences 设置为一个空集合,从而支持默认参数(word 的默认值 nil 是 Java 语言中 null 的 Ruby 版本)。回到 清单 1,在这里必须创建三个构造函数 才能获得 Java 语言提供的相同灵活性!

现在,我用灵活的 initialize() 方法创建另一个 Word 实例,见清单 5:


清单 5. 灵活性的表现!

在定义两个 Definition 之后,将它们添加进一个集合中(集合看起来就像 Java 语言中的数组)。然后将这个集合传递给 Wordinitialize() 方法。





回页首


集合的处理

Ruby 的集合处理同样简单得令人吃惊;看看 Word 类中的 add_definitionadd_synonym 方法就知道了。<< 语法被重载,表示 add。如果再看看 清单 2 中的 Definition 类,就会发现 Java 语言中的对应代码复杂多了:this.exampleSentences.add(exampleSentence)

干净利落的方括号
对于集合,您不认为 Ruby 的 [] 语法非常干净利落吗?如果您是 Groovy 用户,就应该很熟悉这种语法。

Ruby 的集合处理非常简洁。在清单 6 中,可以看到组合集合(使用 + 操作符)和访问成员(通过 [ position ])是多么容易,没有 什么值得操心的东西!


清单 6. 简练的集合

清单 6 中的代码只触及到了 Ruby 集合处理的皮毛!





回页首


RubyBean?

清单 3 中的两个类中您可能会注意到,Ruby 支持一种定义属性的简写方式:attr_readerattr_writer。因为使用了这种方式,所以可以在 Word 类中设置获取 对应的属性,如清单 7 所示:


清单 7. attr_reader 和 attr_writer 的作用

attr_readerattr_writer 都不是关键词,而是 Ruby 中的实际方法(在 Module 类中),它们以符号作为参数。符号 是前面有冒号(:)的任何变量,更妙的是符号本身也是对象!

注意,因为在 清单 3 中使 synonyms 成为只读的,所以 Ruby 拒绝执行清单 7 中的最后一行代码。另外,还可以使用 attr_accessor 方法编写属性声明代码,指出属性是既可读 可写的。





回页首


观察 Ruby 的迭代

灵活的迭代方式也是用 Ruby 编写代码时的乐趣之一。看一下清单 8,这里给出了 Wordinitialize() 方法:


清单 8. 闭包是很方便的

清单 8 的第四行有点儿与众不同。为了让初学者看明白,在 definitions 实例上调用 each 方法时使用了花括号。each 方法本质上就像 Java 语言中的 Iterator,但是它更简洁。在清单 8 中,each 方法处理迭代的细节,使调用者能够将注意力集中在想要的效果上。在这个例子中,传递一个代码块来表示以下意思:对于集合中的每个值 —— 即 idef(这是 Definition 的一个实例),将它的 word 属性设置为 self(这相当于 Java 语言中的 this)。

清单 9 给出 Java 语言中等效的代码行(取自 清单 1 中的 Word 构造函数):


清单 9. Ruby 的 each 方法就像 Java 的 Iterator

是的,是的,是的...!

Java 5 的泛型和新的 for 循环语法比清单 9 中的代码好得多。Ruby 支持大家熟悉的 Java 循环结构,比如 forwhile;但是在实践中很少用到这些结构,因为 Ruby 中的几乎所有东西都支持迭代表示法。例如,在清单 10 中,可以看出迭代文件的内容是多么容易:


清单 10. 迭代非常简单

Ruby 中支持 each 方法的任何类(比如 File)都允许以这种方式进行迭代。顺便说一句,Ruby 的 puts 方法(见清单 10)相当于 Java 语言的 System.out.println





回页首


条件语法

讨论了循环之后,我们来看看 清单 3Word 类中的条件语句。在清单 11 中,单独给出了 add_definition() 方法:


清单 11. 漂亮的条件语法

仔细看看第二行代码。看得出 if 语句的逻辑吗?可以 将它改写为清单 12 所示的一般形式,但是清单 11 不是更好吗?


清单 12. 表达条件有多种方式

在 Java 语言中,如果条件结构的体只有一行,那么可以省略括号。在 Ruby 中,如果条件结构的体只有一行,那么可以编写 清单 11 中所示的表达式。还要注意,同样的条件还可以写成 definition.word = self unless definition.word == self,这使用了 Ruby 的 unless 特性。这很棒,不是吗?





回页首


Ruby 中的多态性

因为 Ruby 是动态类型语言,所以它不需要接口。但是要记住,接口的功能在 Ruby 中是存在的,只是以灵活得多的方式表现出来。Ruby 中的多态性被亲切地称为 “duck typing”(意思是,如果它走起路来像鸭子,叫起来也像鸭子,那么它一定是鸭子!),这种多态性只是对方法名进行匹配的问题。我们来比较一下 Ruby 和 Java 语言中的多态性。

Java 的多态性

在 Java 语言中利用多态性的方式之一是声明一个接口类型,并让其他类型实现这个接口。然后就可以按照接口类型引用实现此接口的对象,并调用这个接口中存在的任何方法。例如,在清单 13 中,定义了一个简单的接口 Filter


清单 13. 简单的 Java 接口

在清单 14 中,定义了一个名为 RegexPackageFilter 的实现类,它应用一个正则表达式来进行过滤:


清单 14. RegexPackageFilter 实现了 Filter

现在,假设有 Filter 接口的多个实现(比如 RegexPackageFilterClassInclusionFilter 类型和 SimplePackageFilter 类型)。为了使应用程序的灵活性最大化,其他对象现在可以引用接口类型(Filter),而不是实现者,如清单 15 所示:


清单 15. 多态性非常酷

Ruby 多态性

在 Ruby 中没有接口!只要方法名匹配,就可以利用多态性。看看吧。

在清单 16 中,用 Ruby 重新创建了 Java Filter 类型。注意,每个类之间并没有关系(只不过它们都拥有同一个方法 apply_filter)。是的,这两个类应该被重构以扩展 Filter 基类;但是,在这里我只是想展示在 Ruby 中如何利用多态性,而类并不共享相同的类型。


清单 16. 过滤我,Ruby!

注意在清单 16 中,可以通过 =~ 语法在 RegexFilterapply_filter() 方法中创建一个正则表达式匹配器。(如果您是 Groovy 用户,就应该熟悉它;清单 16 说明 Groovy 受到了 Ruby 的强烈影响!)

duck typing 的表现

在清单 17 中,我使用 Ruby 的 Test::Unit(这就像 Java 的 JUnit)来演示 duck typing。顺便说一句,在 Ruby 中建立自动测试只需扩展 Test::Unit 并添加以 test 开头的方法。这与 JUnit 很相似,对吗?


清单 17. 多态性过滤

注意,在 test_filters() 方法中,创建了一个包含两个类 SimpleFilterRegexFilter 的集合。这些类并不共享同一个基类,但是在对集合进行迭代时,仍然可以简单地调用 apply_filter() 方法。

还要注意 Ruby 多么轻松地支持正则表达式。要创建正则表达式,只需使用 / regex / 语法。因此,清单 17 中 RegexFilter 的正则表达式是一个大写的 G,后面是一个或多个 o,最后是 gle





回页首


mix-in

Ruby 没有接口,但是它有 mix-in。可以把 mix-in 看成多重继承,但是它没有 多重继承的麻烦。mix-ins 是模块(不能被实例化),其中包含类可以选择包含的方法。这些模块方法会变成包含它们的类的实例方法。

例如,在 JUnit 中,Assertion 类是一个具体的类,其中包含许多 static 断言方法,我们熟悉的 TestCase 类会扩展这些方法。因此,任何 TestCase 实现类都可以在它自己的已定义方法中引用断言方法。

Ruby 的单元测试框架有点儿不一样。它并未定义 Assertion 类,而是定义了一个 Assertions 模块。这个模块定义了许多断言方法,但是 Ruby 不对这个模块进行扩展,Ruby 的 TestCase 类以 mix-in 的形式包含 assertion。因此,这些断言方法现在都是 TestCase 上的实例方法,如 清单 17 所示。





回页首


结束语

您已经看到,Ruby 的语法与 Java 语言很不一样,但是非常容易掌握。另外,某些事情用 Ruby 做起来比用 Java 语言容易得多。

学习多种自然语言的经验告诉我们,可以混合使用不同的编程语言是件好事儿。能够用多种语言进行编程,使您在面对各种编程任务时具有更大的灵活性。这还会提升您的编程母语的价值。

正如在本文开头所说的,我主要是 Java 开发人员,但是我发现有许多办法可以将 Ruby(和 Groovy、Jython 等等)用在工作中。而且这么做的时候不需要使用 Rails!如果您放弃了 Ruby on Rails,因为您实际上不需要 在短时间内构建购物车应用程序,那么好好了解一下 Ruby 本身吧。我认为您会喜欢自己看到的东西。





回页首


参考资料

学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • Programming in the Ruby language” (Joshua Drake,developerWorks,2001 年 7 月):分为四部分的 Ruby 编程简介。

  • Test-first Ruby programming” (Pat Eyler,developerWorks,2005 年 5 月):这个教程介绍用 Ruby 的 Test::Unit 库进行单元测试。

  • 使用 Eclipse 插件 Ruby Development Tools” (Neal Ford,developerWorks,2005 年 10 月):一种在 Java 平台和 Ruby 之间进行衔接的方式。

  • 使用 Ruby on Rails 快速开发 Web 应用程序” (David Mertz,developerWorks,2005 年 6 月):深入介绍这种小型平台。

  • alt.lang.jre: Take a shine to JRuby” (Michael Squillace 和 Barry Feigenbaum,developerWorks,2004 年 9 月):要同时获得 Smalltalk 的面向对象能力、Perl 的表达能力和 Java 类库的灵活性吗?那就试试 JRuby 吧!

  • Programming Ruby: The Pragmatic Programmer's Guide (David Thomas 和 Andrew Hunt;Addison-Wesley,2001 年):这本著名书籍的在线版本。

  • Ten things every Java programmer should know about Ruby (Jim Weirich,O'Reilly Open Source Convention,2005 年):从 Java 开发人员的视角了解 Ruby。

  • 实战 Groovy系列 (Andrew Glover,developerWorks):学习 Groovy 的各个方面 —— 这是一种在 Ruby 的启发下为 Java 平台开发的语言。

  • Java 技术专区:数百篇关于 Java 编程所有方面的文章。


获得产品和技术
  • Ruby 主页:下载 Ruby。


讨论
  • developerWorks blogs:加入 developerWorks 社区!




回页首


关于作者

Andrew Glover 是 Vanward Technologies 的 CTO,这是一家 JNetDirect 公司。Vanward 通过有效的开发人员测试策略和框架、软件测量分析和综合以及持续集成,帮助软件开发团队和管理者持续监视代码质量,从而帮助公司及早 解决软件质量问题。他是 Java Testing Patterns(Wiley,2004 年 9 月)一书的合著者。



转载于:https://www.cnblogs.com/jplee/archive/2006/12/04/581925.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何方便的让你的集合引发改变事件

在我们开发自定义控件的过程中,我们常常会给控件添加集合属性。比如定制Grid控件就会有Column集合。当集合属性发生变化时&#xff0c;比如添加新元素&#xff0c;删除新元素&#xff0c;我们要通知控件去重绘以反映新的变化。我们可以创建一个集合类&#xff0c;在类里添加一个…

[汇编语言]-第八章 div指令,伪指令dd,dup

1- div除法指令 (1) 除数: 有8位和16位两种,在一个寄存器或内存单元中. (2) 被除数: 默认放在AX和DX或AX中 除数为8位, 被除数为16位, 默认在AX中存放. 除数为16位, 被除数为32位, 在DX或AX中存放. AX存放低16位,DX存放高16位. (3) 结果 除数为8位, 则AL存储除法操作的商, AH存…

lambda表达式浅析【C++学习笔记】

lambda表达式浅析【C学习笔记】 基本用法: auto f [/*捕获列表*/](/*参数*/)->int /*后置返回值类型*/{/** 函数体*/};捕获列表: [] : 不捕获任何变量 [变量名] : 表示值捕获,不可修改 [] :按值捕获所有变量,不可修改 [&] : 按引用捕获可以修改 [this] : 在类中捕…

【Cocos2d-x for WP8 学习整理】(2)Cocos2d-Html5 游戏 《Fruit Attack》 WP8移植版 开源...

【Cocos2d-x for WP8 学习整理】&#xff08;2&#xff09;Cocos2d-Html5 游戏 《Fruit Attack》 WP8移植版 开源 原文:【Cocos2d-x for WP8 学习整理】&#xff08;2&#xff09;Cocos2d-Html5 游戏 《Fruit Attack》 WP8移植版 开源这一阵花了些时间&#xff0c;把 cocos2d-h…

碰撞,处理碰撞,发射 Learn Unreal Engine (with C++)

本文使用打砖块游戏举例 碰撞,处理碰撞 碰撞就相当于一个Actor进入另一个Box中,用这个思路就可以处理碰撞了 OnComponentBeginOverlap 当某些内容开始重叠此组件时调用的事件&#xff0c;例如玩家进入触发器。 **委托 事件 **1 AddDynamic( UserObject, FuncName ) 用于…

传送,条件加速 Learn Unreal Engine (with C++)

本文以吃豆人游戏为例UE4项目: 自制UE4 小游戏 (gitee.com) 传送 pawn进入box触发OnActorBeginOverlap获取目标位置,下一帧将pawn坐标更改为目标位置 首先需要重叠函数与开始重叠事件绑定 OnActorBeginOverlap.AddDynamic(this, &ATeleporterActor::OnOverlapBegin);头文件…

获取摄像机,摄像机切换Learn Unreal Engine (with C++)

摄像机应该是使用最普遍的组件了 获取摄像机,摄像机切换 新建C类(以CameraActor为父类) 将摄像机在地图中放置 头文件声明 virtual void BeginPlay() override;UPROPERTY(EditAnywhere, BlueprintReadWrite)UBoxComponent* OverlapVolume; // 盒体组件,用于检测人物碰撞UPR…

android报错及解决1--Bitmap加载时,报bitmap size exceeds VM budget

报错描述&#xff1a; 用Bitmap加载图片资源时&#xff0c;报错java.lang.OutOfMemoryError: bitmap size exceeds VM budget 原因分析&#xff1a; android系统限制&#xff0c;只给图片分配8M内存&#xff0c;超过就蹦。图片虽然几十K&#xff0c;可能是压缩格式&#xff0c;…

主角的创建与选择 Learn Unreal Engine (with C++)

主角创建有两种方式,本教程以SpaceshipBattle fanxingin/UE4项目 - 码云 - 开源中国 (gitee.com) 1. 新建游戏模式方式 新建一个蓝图类,选择游戏模式基础 在蓝图类的细节中将默认pawn类选择主角的蓝图类 在项目设置->地图和模式->默认模式->默认游戏模式 默认游…

控制`Actor`朝向,运动 Learn Unreal Engine (with C++)

控制Actor的朝向,以及Actor的运动 SpaceshipBattle fanxingin/UE4项目 - 码云 - 开源中国 (gitee.com) 控制Actor朝向鼠标 设置鼠标在游戏中可见 获取玩家控制器鼠标可见设置为true PC Cast<APlayerController>(GetController()); PC->bShowMouseCursor true;获取…

.Net开发人员应该下载的十种必备工具(三)

NDoc 编写代码文档资料几乎总是一项令人畏惧的任务。我所说的不是早期设计文档&#xff0c;甚至也不是更为详细的设计文档&#xff1b;我说的是记录类上的各个方法和属性。NDoc 工具能够使用反射来分析程序集&#xff0c;并使用从 C# XML 注释生成的 XML 自动为代码生成文档资料…

Actor范围内随机生成 Learn Unreal Engine (with C++)

Actor范围内随机生成 Learn Unreal Engine (with C) SpaceshipBattle fanxingin/UE4项目 - 码云 - 开源中国 (gitee.com) Actor范围内随机生成 新建box组件 SpawnArea CreateDefaultSubobject<UBoxComponent>(TEXT("SpawnArea"));RootComponent SpawnArea…

.Net开发人员应该下载的十种必备工具(二)

NUnit NUnit 是为 .NET 框架生成的开放源代码单元测试框架。NUnit 使您可以用您喜欢的语言编写测试&#xff0c;从而测试应用程序的特定功能。当您首次编写代码时&#xff0c;单元测试是一种测试代码功能的很好方法&#xff0c;它还提供了一种对应用程序进行回归测试的方法。NU…

子弹创建及发射 Learn Unreal Engine (with C++)

子弹创建及发射 Learn Unreal Engine (with C) SpaceshipBattle fanxingin/UE4项目 - 码云 - 开源中国 (gitee.com) 子弹的创建 声明: UPROPERTY(EditAnywhere, Category "Fire")TSubclassOf<ABullet> Bullet;实现: //在空组件处生产子弹GetWorld()->…

.Net开发人员应该下载的十种必备工具(一)

用于编写单元测试的 NUnit用于创建代码文档资料的 NDoc用于生成解决方案的 NAnt用于生成代码的 CodeSmith用于监视代码的 FxCop用于编译少量代码的 Snippet Compiler两种不同的转换器工具&#xff1a;ASP.NET 版本转换器和 Visual Studio .NET 项目转换器用于生成正则表达式的 …

旋转根组件 Learn Unreal Engine (with C++)

旋转根组件 Learn Unreal Engine (with C) 在UE4中,根组件是无法旋转定位的,只能够缩放,在一些情况下,我们有旋转根组件的需求 SpaceshipBattle fanxingin/UE4项目 - 码云 - 开源中国 (gitee.com) 旋转根组件 将SceneComponent设为根组件 然后将StaticMeshComponentattach…

2007.2.14 日程安排

公元二零零七年二月十四日&#xff0c;农历腊月二十七&#xff0c;该天尤为特别&#xff0c;乃春节长假之初始。此外&#xff0c;该天将是片地鸳侣&#xff0c;漫天桃花之好时日&#xff0c;于是吾将广纳四方真气&#xff0c;闭关修炼&#xff0c;与世无争。00&#xff1a;00 -…