翻译 - 元编程动态方法之public_send
2019独角兽企业重金招聘Python工程师标准>>>
李哲 — MAY 20, 2015 原文地址:Metaprogramming Dynamic Methods: Using Public_send
作者:Friends of The Web的开发者Vaidehi,OneAPM官方技术博客编译整理。
在上周,我写了一些让我感到非常骄傲的代码!当时,我正努力解决一个有趣的问题,这个问题也是我最近开发的一款应用中所遇到的。于是我把脑海中想到的第一种解决办法很快付诸了实践。然后,当我回过头来查看文本编辑器,并认真审阅完自己所写的代码时,终于意识到:这些代码真的很赞!
一周以来,我不断回顾那些代码段,沉思到底是什么原因令她如此美丽。而我又做了什么不一样的事情,竟让自己的内心充满了骄傲。我认为是元编程,这是我能够第一时间想到的答案。元编程(Metaprogramming)是指某类计算机程序的编写,这类计算机程序编写或者操纵其他程序(或者自身)作为它们的数据,或者在编译时完成部分本应在运行时完成的工作。当然,其中涉及到很多不同的技巧和方法,我也不是专家。但是我的确学到了一个元方法 public_send ,接下来我分享一下自己的使用心得。
GOTTA DISPATCH? DO IT DYNAMICALLY.
其实,不仅仅只是Ruby,编程世界中的一切都只是一种抽象。我们只不过对那些晦涩难懂的机器语言包裹了一层“糖衣”,让程序员的生活变得更为简便,让代码更具美感罢了。但归根结底,这只是对其他事物的一种抽象而已。当我们通过元编程进行重构时,理解的是同样的抽象概念,而且我们必须牢记这一点。同时,当我们试图发现代码中的共性问题时,这也意味着,我们可以封装并抽离那段代码的部分功能。
我最喜欢的抽象实例是method dispatching,通过这种方法我们将信息传递给一个对象,当然,这也是我们经常做的事情。因为Ruby中的所有事物都是一个对象,只要你想调用某个对象完成某件任务,你就不得不向它发送信息。我们应该庆幸,因为Ruby是如此强大,我们用来发送信息的方法就刚好就叫做:send
。
其实,send方法在程序中被调用的次数远超过我们的想象。比如,当我们打开操控台,做一些简单的数学运算:
2.2.0 > 3 + 4
=> 7
而我们真正做事情的是:给整数3这个对象发送一个信息,告诉它与另一个对象(整数4)进行加法动作。
2.2.0 > 3.send(:+, 4)
=> 7
send 方法用字符串或者一个符号为参数,并以此做为方法名。换言之,方法名总是第一个参数,而第二个参数将以自变量的形式传给该方法。
此时,如果你想执行3加4的加法操作,就会变得很容易。但是,谁会一直做如此简单的加法计算呢?显然没有。你很可能还会执行3加5,加6,一直加到无穷等等。
而使用dynamic dispatching 就可以拯救你!
Dynamic dispatching
,就如同上面这幅略显奇怪但是相当可爱的动态图,因为涉及向对象发送不同的信息(read: methods
) ,同时因情况的差异,还会产生方法不断改变的警告。Dynamic dispatching
允许我们在程序中面向对象发送不同的方法,而且无需告知其他对象所发送信息的内容。如果你需要再一个特定的环境下调用一个方法,但是不清楚该方法是如何执行的,那么此时就是Dynamic dispatching
出场的最佳时机。
我们可以看一下这个例子。
YOU CAN SEND WHUTEVA YOU LIKE
你可以使用 send
方法,来向一个对象“send”不同的方法,但这只占据了一半的乐趣,另一半在于搞清楚什么时候去实际的应用它。
此刻,让我来给大家展示一下,最近如何在应用中使用 Dynamic dispatching
来触发特定的方法。在本文中,我使用了一个电子书店的例子,希望大家能够接受。
在我的书店中,拥有一个开放购买的图书列表,列表以页码为单位来进行展示,且每本书在界面中只有有限的展示空间。作为网站的管理员,我必须决定针对不同的书应该如何进行更好的展示。一些书有非常漂亮的封面,我会想用封面的缩略图作为他们主要的“viewable attribute”,因为我使用了 paperclip
插件,这点很容易实现。
然而,有些书压根就没有封面。比如,浩如烟海的莎士比亚戏剧集,如果以“作者”做为“viewable attribute”,能更好地能吸引读者。而《权力的游戏》系列图书若以书名作为“可见属性”,显然更具吸引力。
所以,我该如何处理这个问题呢?首先,让我们看一下有没有任何共性的存在。
1. Look For Patterns 诚然,我们都希望界面中的每个 Book
对象都能用它最主要的可见属性来展示。我们遇到的问题是管理员会为每个 Book 对象选择不同的属性,再设置其为“viewable
”,因此我们无法预测这个属性是title
,author
,还是是一张图像。但是,我们的确知道每个 book
对象都需要某个“viewable attribute
”。
这点很酷!所以这里就发现了一个共性:我们需要展示一个属性,但是我们并不知道它会是什么。或者说,其实我们知道?
2. Consider The Data 当下我们为该应用建立一个管理员界面时,我们清楚的知道每本书都有一个title
,一个author
。书的封面可以是有选择性的(此处我们称其为media
),但另外两个属性则不一定。这意味着我们要对 Book
对象进行一次验证:
class Book < ActiveRecord::Basevalidates_presence_of :title, :authorend
经过验证值之后,我进一步思考Book
对象肯定也会有的其他属性,首先进入我脑海的就是viewable_by
属性。我们可以设想一下,管理员必须将某个属性设为“viewable
”,当他更新此对象时,“viewable
”就可能改变。因此,对每个 Book
对象来说都是独一无二的,这也意味着我们可以放心地将其保存在数据库中。
所以,我们可以通过代码迁移在数据库中增加viewable_by
属性,并且设置成不可为null
,同时将默认值设为 Book’s title
:
class AddViewableByToBooks < ActiveRecord::Migrationdef changeadd_column :books, :viewable_by, :string, null: false, default: "title"endend
这个迁移看起来非常简单,但是,but it is its very simplicity that lends itself so elegantly to some serious metaprogramming that we’ll do next.
3. Encapsulate And Abstract 最后这部分的确最难理解,但也最炫酷。现在,我们的数据库里多了一列,且字符串类型的值是title
, author
,media
中的一个,管理员可以随意改变或更新这些值。显而易见,这些值的更改不可避免,但是有一点不变:we’re still going to want to render the value of whatever attribute is marked as “visible” – that is to say, whatever string value is saved as viewable_by
.
此时,我们可以回头想想之前定义的那个共性。我们知道,属性会不断改变,但是我们对它的操作仍然保持了一致性。不论Book
对象的viewable_by
属性是什么,我们都会展示这个属性。而且我们会跟该对象发送信息:“嘿,书先生,你可见的那个属性,不论它是什么,都是你用来展示自己的值!”
And this is where we can use send
to encapsulate and abstract this away into a single method call。首先,我们要增加一个方法来检查可见属性是不是一个图象,如果是的话,我们会将其交给paperclip
插件来进行展示:
def show_cover?self.viewable_by == 'media'end
如果viewable_by属性的值为media,该方法会返回true,否则返回false。我们可以将这个布尔返回值用在一个条件语句中:
def book_htmlif show_cover?# Code here will generate and return# an html image tag to render in view.elsesend(self.viewable_by)endend
上面的代码怎么了?如此炫酷!book_html
方法要么展示一张缩略图(我们会写别的代码来实现),要么返回一个title
对象或者author
对象。当然,真正炫酷的地方在于,我们可以在表中添加其他属性,像year或者genre,并在此基础上显示html网页,只需要这些属性保存在viewable列中。
这到底是怎么运行的?其实,每次我们在数据库里创建新列时,我们都获得了两个重要的方法:读和写。这意味着我们拥有了title=
和 title
两个方法。
如果我们回顾一下 send
方法的工作原理,就会知道send
会将一个字符串或一个符号作为参数,该参数也是被调用的方法名。当我们调用 send
方法并将self.viewable_by
的值传给它时,我们实际上是在调用一个Book
实例的send
(“title
”) 方法。这会调用该Book
实例的title
属性,并将那本书的书名以字符串的形式返回。
这段代码炫酷的地方在于它非常灵活,同时还能将一种共性抽象为动态的方法调用,只在合适的时间对恰当的对象发起请求。但是,这段代码中还存在一个大问题,接下来让我们把它解决掉。
TO SEND OR TO PUBLIC SEND? THAT IS THE QUESTION
很多控诉 send
方法的证据都源于此: send
会将private methods
发送给对象。这在应用内部是相当危险的,而且也让应用在应对外部恶意攻击时显得相当脆弱。
一种快速的修正方法就是使用 pubic_send
,它的功能与你所想的完全一致:只将公共可读取的方法发送给它的接收对象,所以我们最终的代码如下:
class Book < ActiveRecord::Basevalidates_presence_of :title, :authordef show_cover?self.viewable_by == 'media'enddef book_htmlif show_cover?# Code here will generate and return# an html image tag to render in view.elsepublic_send(self.viewable_by)endendend
赞!作为我们元编程的首次尝试,这段代码并没有显得很寒碜。
虽然做起来很难,但在重构和元编程方面,你也不必太苛求自己。老实说,随着时间的累积,你可以多加练习,逐渐的增长见识,那么编程能力自然也能够得到提高。最终,你会开始察觉那些出现了一次又一次的共性,并开始学会选择正确的工具来解决这些问题。
尽管需要额外的努力,我认为尝试不同的元编程技巧对未来大有裨益,多读多写代码亦是如此。重写之前的代码来实现一些元编程技巧,你可以修改应用中过于死板的代码,将之变得更为灵活、动态。
如果这些话听起来有些吓人,那是因为事实本就如此!但是这并不是不可能的,正如我最近取得的进展,我也希望正在阅读此文的你也能有所斩获。幸运的是,Ruby提供了很多工具,帮我们将死板的代码实现了元程序化。而问题就在于了解那些工具,并在合适的时机能够使用它。我相信,如果你体验了一次元编程,你肯定会欣喜万分,也许还会像小猫一样发出欢乐的尖叫,我想那将是世上最可爱的事情了。
总结一下
在编写程序时,我们可以使用dynamic dispatching
将一个方法传递给对象,而不必指明方法的内容。方法send
和 pubic_send
都能实现这一点,它们以字符串或符号为参数,并以此参数作为接受对象调用的方法名。 点击这里学习元编程的基本要素,并查看send
和pubic_send
方法的相关文档。 如果你还好奇其他的动态方法?可以阅读一下这篇博文,其中深度介绍了一部分方法。
本文作者系OneAPM工程师 ,想好的技术文章,请访问OneAPM官方技术博客。
转载于:https://my.oschina.net/oneapmofficial/blog/418004
翻译 - 元编程动态方法之public_send相关推荐
- 【Groovy】MOP 元对象协议与元编程 ( 方法注入 | 使用 Category 分类注入方法 )
文章目录 一.方法注入 二.使用 Category 分类注入方法 三.完整代码示例 一.方法注入 在之前的博客中 , 主要是使用 Groovy 元编程 拦截方法 , 改变方法的实现 ; 使用元编程还可 ...
- python元编程运用_Python 中的元编程
就像元数据是有关数据的数据一样,元编程就是编写用于操纵程序的某些程序.人们普遍认为,元程序就是生成其他程序的某些程序,但范式更加广泛.所有旨在自我读取.分析.转换或修改的程序都是元编程的范例.例如: ...
- 浅谈 C++ 元编程
随着 C++ 11/14/17 标准的不断更新,C++ 语言得到了极大的完善和补充.元编程作为一种新兴的编程方式,受到了越来越多的广泛关注.结合已有文献和个人实践,对有关 C++ 元编程进行了系统的分 ...
- 【Groovy】Groovy 语言特点简介 ( 支持 Java 语法 | 支持 Java 虚拟机 | Groovy 语言是动态语言 | Groovy 扩展 JDK | 编译时元编程 )
文章目录 一.Groovy 支持 Java 语法 二.Groovy 支持 Java 虚拟机 三.Groovy 语言是 动态语言 四.Groovy 扩展 JDK 五.Groovy 编译时元编程 一.Gr ...
- python元编程之使用动态属性实现定制类--特殊方法__setattr__,__getattribute__篇
问题:实现一个类,要求行为如同namedtuple:只存在给定名称的属性,不允许动态添加实例属性. 主要知识点在于: __setattr__,__getattr__,getattribute__,__ ...
- 【Groovy】编译时元编程 ( 编译时方法注入 | 使用 buildFromSpec、buildFromString、buildFromCode 进行方法注入 )
文章目录 一.在 MyASTTransformation#visit 方法中进行方法注入 1.使用 new AstBuilder().buildFromSpec 进行方法注入 2.使用 new Ast ...
- 【Groovy】编译时元编程 ( 编译时方法拦截 | 在 MyASTTransformation#visit 方法中进行方法拦截 )
文章目录 一.在 MyASTTransformation#visit 方法中进行方法拦截 二.完整代码示例及进行编译时处理的编译过程 1.Groovy 脚本 Groovy.groovy 2.ASTTr ...
- 【Groovy】编译时元编程 ( 方法拦截时用到的 AST 语法树节点 MethodNode 节点 | MethodNode 节点分析 | MethodNode 节点中的BlockStatement)
文章目录 一.方法拦截时用到的 AST 语法树节点 MethodNode 节点 二.MethodNode 节点分析 三.MethodNode 节点中的 BlockStatement 集合 一.方法拦截 ...
- 【Groovy】编译时元编程 ( ASTTransformation#visit 方法简介 | org.codehaus.groovy.ast.ModuleNode 脚本节点 )
文章目录 一.ASTTransformation#visit 方法简介 二.org.codehaus.groovy.ast.ModuleNode 脚本节点 一.ASTTransformation#vi ...
最新文章
- 强大的Charles的使用,强大的flutter1.9
- “Can’t be opened because Apple cannot check it for malicious software“ 解决方案
- Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
- mysql预编译语句拼接查询_SQL语句预编译(查询)
- cxxtest单元测试框架源码分析(二):所有对外功能实现分析
- 学习Spring Boot:(十三)配置 Shiro 权限认证
- golang 目录分隔符号_Golang 从0到1之任务提醒(一)
- web大前端开发中一些常见的安全性问题
- 20191226每日一句
- 【渝粤教育】电大中专建设工程法规_1作业 题库
- 谷歌io开发者大会2018:强大的AI帝国
- 计算机图形学 构成立方体数据结构的简单表结构,计算机图形学-沈工大作业管理ppt课件...
- 真人语音朗读软件_讯飞语音云助力移动“和阅读”,打造个性化听书应用
- 公众号(服务号)申请与认证
- L3-015 球队“食物链” (30 分)
- 服务器上搭建java环境,安装tomcat以及MySQL数据库-小白教程
- 使用破解补丁激活过期的pycharm
- 淘系电商再无对手,腾讯为何“资敌”?
- 陌生QQ号聊天,QQ咨询对话框,QQ临时对话框链接代码
- SSE图像算法优化系列二十五:二值图像的Euclidean distance map(EDM)特征图计算及其优化。...
热门文章
- pcre的compile,exec和free的代码
- Hasura GraphQL 内部表结构
- Linux内核【链表】整理笔记(1)
- 长按UIWebView上的图片保存到相册
- 《jQuery Mobile入门经典》—— 2.2 展现CSS样式
- SharePoint 2013 Step by Step——使用自定义的List Template
- 删除windows server backup备份
- table或者列表中超出的字用省略号代替的方法(支持IE6)
- redhat as4 上安装 MySQL5
- 解决“鼠标关机后仍然发光”的方法