ruby正则表达式:Ruby 101:重用、隐藏和多态

  什么?你不想安装Ruby?

  在我决定把Ruby装到我机子里的前我想先试下;在我试用Ruby的前我得先把它装到我机子里;在我决定……哎哟死锁了……

  没问题今天我带上"钥匙"了!现在请用你喜欢浏览器打开这个网址:http://tryruby.sophrinix.com/你将会看到个网页版irb:



  图片看不清楚?请点击这里查看原图(大图)

  图 1

  现在它已经支持到Ruby 1.9.0了而且还有自动缩进虽然说你可以使用你喜欢浏览器来打开它但实际上能用浏览器并不多机子里同时安装了IE8、FireFox 3.5、Google Chrome 3.0、Opera 10.0、Safari 4.0和Maxthon 2.5全部更新到最新但使用正常只有IE8、FireFox 3.5和Maxthon 2.5在Google Chrome 3.0和Safari 4.0上方向键失灵(同行)无法回头修改而在Opera 10.0上按下左右2个方向键居然分别输出%和'……

  本来IronRuby也有个用Silverlight做iirb以前我试过能用可现在打开的后就空白片了(我机子里安装是Silverlight 3.0)我不知道为什么有兴趣话你也可以试地址是:http://ironruby.codeplex.com/Wiki/View.aspx?title=SilverlightInteractiveSession

  虽然现在你不用安装Ruby也能体验把了可你不要对它抱有不切实际幻想哟毕竟它不是装在本机irb如果你要在里面读/写文件、访问 SQLite或者MySQL那么你注定要失望了然而对于这个系列头几篇文章我相信它还是胜任有余接下来就让我们进入今天主题吧!

  我不喜欢重复劳动!

  在上篇文章里我们了解到Ruby允许你"重新打开"个类并对里面内容进行修改我们还通过这种办法扩展了由Struct类创建Book类(参见Ruby 101:类和对象代码12)此外我还提到可以通过继承来扩展Book类下面我们来看看如何在Ruby里实现继承:



  代码 1

  Ruby通过<符号来表达继承关系上面代码创建了个Book1类它继承自由Struct类创建Book类此外这两部分代码还可以合 2为:



  代码 2

  这相当于你把创建属性工作交给Struct类你自己则专注于包含业务逻辑思路方法

  无论你以什么方式看待继承为它赋予何种层次意义你肯定不会否认它重用功效般而言面向对象语言都支持继承和组合这两种重用方式而Ruby还支持第 3种——Mixin那么什么是Mixin呢?设想我要为Book类增加计算折扣功能毫无疑问最简单办法就是把代码直接写到Book类里:



  代码 3

  显然这部分代码还可以用到其它地方把它直接写到Book类里会使它丧失重用性我们可以考虑把它放到辅助类里然后通过组合在Book类里重用它但这需要额外代码来重定向相关思路方法我们也可以考虑把它放到基类里然后通过继承在Book类里重用它可是Ruby不支持多重继承如果将来我们发现另个类更合适成为Book类基类或者像代码2Book类那样已经有基类了我们就会陷入困境那么我们该如何解决这个问题?聪明你肯定猜到我想说什么了现在轮到Mixin出场了下面我们来看看它是如何协助我们应对这种情况

  首先把代码抽出来放在个单独模块里:



  代码 4

  接着通过在Book类里把它包含进来:



  代码 5

  至此我们已经通过Mixin完成了代码重用那么模块里代码该如何用呢?非常简单你把它们想象成通过代码3方式创建然后该如何用就如何用:



  代码 6

  噢这简直就是你无法分辨这些思路方法究竟是直接在Book类里创建还是通过Mixin混进去太神奇了!

  在上篇文章里我们看到模块可以用作命名空间而在本文里我们看到模块可以用作Mixin这是模块两个典型用途它们均向我们展示了模块作为代码容器这个特征到目前为止我们已经看到模块可以包含类(参见Ruby 101:类和对象代码4)和例子思路方法(参见本文代码4)此外模块还能包含什么呢?

  首先我们来试试静态思路方法还记得如何在类里创建静态思路方法吗如果忘记了不要紧回去上篇文章复习下吧在上篇文章里我们看到在类里创建静态思路方法做法有4种下面我们尝试照搬头两种也是最有可能行得通两种:



  图 2

  实战证明这两种做法都行得通另外我们还看到模块静态思路方法和类静态思路方法在上也是.和::都可以用来连接模块名/类名和思路方法名至于使用哪基本上是个偏好问题那么剩下两种呢?嗯我觉得不太可能但尽管试下吧:



  图 3

  噢出错了!在类里面使用 << self那么换了模块不是应该变成module << self吗?可实战证明这是行不通难道在模块里创建静态思路方法只有上面两种方式?突然脑子蹦出个怪主意直接在模块里使用 << self会怎样呢?来我们试下:



  图 4

  这……够呛!现在脑子里只有个疑惑:最后种做法是不是也会这样?我们试下吧:



  图 5

  哎哟不错嘛!这样看来创建静态思路方法做法对于模块和类来说是通用

  如果我把个同时带有例子思路方法和静态思路方法模块通过包含到个类里又会怎样呢?我是否可以通过这个类及其例子分别访问这个模块静态思路方法和例子思路方法?想知道答案吗?我们做个试验吧:



  图 6

  从上图可以看到模块静态思路方法只能通过模块来访问而模块例子思路方法则可以通过类例子来访问事实上这也是访问模块例子思路方法办法模块是没有例子换言的模块例子思路方法是用于Mixin而静态思路方法则可以看作辅助思路方法

  接下来我们试试常量字段和例子字段:



  图 7

  在Ruby里常量字段以大写字母开头般建议全部大写单词的间用下划线(_)分隔比如说MY_CONSTANT;例子字段则和类还记得吗例子字段在使用的前无需事先声明于是我创建个思路方法来使用@var1这个例子字段下面我们创建个类来包含这个模块:



  图 8

  由于例子字段是私有我们需要创建个思路方法来设置它此外_var1思路方法和show_var1思路方法执行结果也将会告诉我们它们是否使用着同个例子字段现在我们来看看常量字段和例子字段能否如我们期望运作:



  图 9

  从上图可以看到 _var1思路方法里@var1和show_var1思路方法里确实是同个例子字段此外我们还看到模块里常量字段既可以通过模块名来引用也可以通过类名来引用但模块名/类名和常量字段的间必须使用::来连接

  现在请研究个问题:@var1这个例子字段是在Module1里创建然后Mixin到Class1里吗?还是说@var1这个变量"出生"和_var1思路方法和show_var1思路方法顺序有关?下面我们做个试验看看:



  图 10

  这个试验设计思路是这样我分别在Module1和Class1里创建个例子思路方法这两个例子思路方法时首先通过instance_variable_d?思路方法检查@var1这个例子字段是否存在如果不存在就用相应标识串来化它然后把@var1内容打印出来现在我们来看看顺序会否影响@var1创建:



  图 11

  执行结果已经非常直白地把结论告诉我们了这意味着单纯地看图7和图8代码是无法确定@var1这个例子字段最终是来自谁对于图9来说我们可以说@var1是来自Class1如果我们在c._var1(Module1::CONSTANT1)的前加上句c.show_var1会不会改变@var1"出生"?我们不妨试试看:



  图 12

  从上图可以看到show_var1并未导致@var1创建换言的在c._var1(Module1::CONSTANT1)的前加上句c.show_var1不会改变@var1"出生"此外我们还发现个有趣现象对于个刚刚创建出来Class1例子它没有任何例子字段这是我们没有使用initialize思路方法在创建Class1例子时化相关例子字段这意味着例子字段是可以按需创建而不必在开始就固定下来但同时也意味着你需要掌握好例子字段创建时机以及管理好它们创建顺序当然你也可以放弃按需创建好处通过initialize思路方法来固定所有例子字段这样你就不必担心例子字段创建问题了

  最后我们来看看模块中模块为了体现效果我嵌套了3层:



  代码 7

  那么我们该如何"到达"Module3show思路方法呢?有3种途径:



  代码 8

  第1种是直接通过模块名层走进去各层模块名的间通过::连接;第2种通过把Module1包含进来这样Module1里面Module2就可以在当前上下文直接引用了;第3种则通过把Module2包含进来这样Module2里面Module3就可以直接引用了

  那么是不是说有多少层就得创建多少个(嵌套)模块呢?是也不是嗯?如何理解?我们不妨做个试验:



  图 13

  显然这是行不通但是请别就此放弃细读信息它告诉我们Module1未被这是否意味着我们得先把Module1化了呢?如果是我们就先化Module1和Module2吧:



  图 14

  成功了!从上图可以看到Ruby支持在创建模块时把各层名字通过::连起来条件是外层模块得先存在

  嘘别让他们知道……

  到目前为止我们创建思路方法都是公有这显然是不够我们很多时候都需要限制外界对思路方法访问那么如何创建私有思路方法?最简单做法就是在你想使的变成私有思路方法上标上private比如说我现在有1个Class1类里面有4个思路方法:



  代码 9

  我想把method2思路方法变成私有那么我可以在它上面标上private:



  代码 10

  但是这将会导致method3思路方法和method4思路方法都变成私有在这里private就像个开关会把它下面思路方法都变成私有如果我只想把method2思路方法和method3思路方法变成私有那么我需要在method4思路方法上面标上public:



  代码 11

  至于method1由于它上面没有任何访问控制标识将会默认为公有当然你也可以在每个思路方法上面显式标上private或public如果思路方法比较多你可能会发现private和public交错地穿插在思路方法的间如果你觉得这样比较乱可以按照访问级别重新组织这些思路方法:



  代码 12

  此外我们也可以这样做:



  代码 13

  代码12和代码13是等效你可以根据个人喜好选择任意种做法事实上private是个思路方法如果你不向它传递任何参数它将会把它和类结束标记(end)或者另个访问控制标识(比如说public)的间所有思路方法变成私有代码12就是这种情况;它也可以接受参数当你把思路方法名以Symbol(或者串)方式传给它时它将会把对应思路方法变成私有代码13就是这种情况

  我们知道私有思路方法只能在内部但你可能不知道在私有思路方法时有个规则需要遵守我们来看看下面代码这里method1思路方法里self可以看作C#this凭直觉说你觉得method1思路方法时会怎样?



  代码 14

  现在我们执行看看你是否猜对了:



  图片看不清楚?请点击这里查看原图(大图)

  图 15

  从上图可以看到method1思路方法头两行代码正常执行了唯独是第 3行——self.method2此外我们还看到执行这行代码所报错和在外面直接method2思路方法所报错是为什么会这样?这要从Ruby如何限制私有思路方法说起在Ruby里个思路方法其实就是向某个对象发送消息般情况下这是通过obj.method来完成如果我们把前面obj.省略那么这个消息就会发向默认对象(又称当前对象)用self来表示自己私有思路方法意味着向自身发送消息当我们在内部私有思路方法时默认对象恰好就是私有思路方法所属对象在这种情况下把obj.省略会使消息发向默认对象即私有思路方法所属对象因而不会影响消息传递;当我们在外部私有思路方法时默认对象并非私有思路方法所属对象这意味着消息发送者并非自身Ruby正是通过强制把obj.省略来确保私有思路方法得到恰当任何你可以私有思路方法地方都是可以把obj.省略掉这意味着你也不能像代码14那样通过self.来私有思路方法

  然而这个规则有个例外我们来看看下面代码:



  图 16

  在上篇文章里我们了解到属性写访问器实际上会被解析为思路方法即当Ruby碰到c.var1 = "What's var1?"时会把它解析成c.var1=("What's var1?")而var1=正是attr_accessor为var1属性创建写访问器名字既然是思路方法而且发生在内部理应无需指明消息接受者(即省略obj.)那么当我method1思路方法的后var1属性值是什么?我们来试下吧:



  图 17

  从上图可以看到method1思路方法里那句根本就没有被解析为var1属性写访问器那么method1思路方法里那个var1是什么?你觉得呢?你如何知道我是想var1属性写访问器而不是对var1本地变量进行赋值?事实上你无法确定!Ruby把这句解析为后面那种情况这就是为什么我们method1思路方法的后var1属性值是nil如果你是使用NetBeans来编写Ruby代码NetBeans将会检测到这个问题并通过相关标识来提醒你:



  图 18

  那么我们应该如何告诉Ruby我们期望是前面那种情况?答案是通过self:



Tags:  ruby正则表达式

延伸阅读

最新评论

发表评论