如何利用 Objective-C 写一个精美的 DSL ?
Posted InfoQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何利用 Objective-C 写一个精美的 DSL ?相关的知识,希望对你有一定的参考价值。
在程序开发中,我们总是希望能够更加简洁、更加语义化地去表达自己的逻辑,链式调用是一种常见的处理方式。我们常用的 Masonry、 Expecta 等第三方库就采用了这种处理方式。
像这种用于特定领域的表达方式,我们叫做 DSL (Domain Specific Language),本文就介绍一下如何实现一个链式调用的 DSL.
我们举一个具体的例子,比如我们用链式表达式来创建一个 UIView,设置其 frame、backgroundColor, 并添加至某个父 View。
对于最基本的 Objective-C (在 ios4 block 出现之前),如果要实现链式调用,只能是这个样子的:
有了 block,我们可以把中括号的这种写法改为点语法的形式
可以看出,链式语法的语义性很明确,后者的语法更加紧凑,下面我们从两个角度看一下后者的实现。
链式调用可以用两种方式来实现:
1.在返回值中使用属性来保存方法中的信息
比如,Masonry 中的 .left .right .top .bottom
等方法,调用时会返回一个 MASConstraintMaker
类的实例,里面有 left/right/top/bottom
等属性来保存每次调用时的信息;
再比如,Expecta 中的方法 .notTo
方法会返回一个 EXPExpect
类的实例,里面有个 BOOL 属性 self.negative
来记录是否调用了 .notTo
;
再比如,上例中的 .with 方法,我们可以直接 return self;
2.使用 block 类型的属性来接受参数
比如 Masonry 中的 .offset(15)
方法,接收一个 CGFloat 作为参数,可以在 MASConstraintMaker
类中添加一个 block 类型的属性:
比如例子中的 .position(x, y)
,可以给的某类中添加一个属性:
在调用 .position(x, y)
方法时,执行这个block,返回 ViewMaker 的实例保证链式调用得以进行。
从语义层面上,需要界定哪些是助词,哪些是需要接受参数的。为了保证链式调用能够完成,需要考虑传入什么,返回什么。
还是以上面的例子来讲:
分步来看一下,这个 DSL 表达式需要描述的是一个祈使句,以 Alloc 开始,以 intoView 截止。在 intoView 终结语之前,我们对 UIView 进行一定的修饰,利用 position
size
bgColor
这些。
下面我们分别从四段来看,如何实现这样一个表达式:
在 AllocA(UIView) 的语义中,我们确定了宾语是 a UIVIew。由于确定 UIView 是在 intoView 截止那时,所以我们需要创建一个中间类来保存所有的中间条件,这里我们用 ViewMaker 类。
另外我们可以注意到AllocA是一个函数,而UIView无法直接传递到这个函数中,语法就要变成 AllocA([UIView class])
而失去了简洁性。所以我们需要先定义一个宏来“吞”掉中括号和 class
这个方法:
很多时候,为了让 DSL 的语法看起来更加连贯,我们需要一些助词来帮助,例如 Masonry 里面的 make.top.equalTo(superview.mas_top).with.offset(padding.top) 这句中的 with 就是这样一个助词。
而这个助词和我们学过的语法一样,通常没有什么实际效果,简单返回self就可以。
需要注意的是,返回自己,就没有办法阻止用户不断调用自己 .with.with.with
,为了避免这种情况,可以新生成一个类,每个类都拥有自己所在层次的方法,避免跃层调用。
这样就有效防止了,.with.with.with
这样的语法。但是实际上,我们要根据真实的需要来进行开发,使用 DSL 的用户是为了更好的表达性,所以并不会写出.with.with.with
这样的代码,这样的防护性措施就显得有点不必要了。
不过使用类来区分助词还有另外几个小好处,就是它可以确保在语法提示的时候,ViewClassHelper
这个类只有.with
这样一个语法提示,而ViewMaker
不出现.with
语法提示;并且同时确保.with
一定要出现。
不过为了简化文章,我们都使用前者,既.with
返回self
来继续下文:
像例子中的position
size
bgColor
这些都是定语部分,用来修饰UIView,他们以属性的形势存在于ViewMaker
的实例中,为了支持链式表达,所以实现的时候,都会继续返回self
。
我们来试着实现下:
“终结词”这个实在是在现代语法里面找不到对应关系了,但是在 DSL 中,这一段尤为重要。ViewMaker
的实例从头至尾收集了很多的修饰,需要最后的一个表达词语来产生最后的结果,这里就称为”终结词”。例如在 Expecta 这个开源库里面的 equal
就是把真正的行为表现出来的时候,to
和 notTo
都不会真正触发行为。
在我们的例子里,终结词.intoView(aSuperViwe)
可以这样实现:
这样,一个终结词就写好了。
最终代码的汇总:
这种链式调用能够使程序更加清晰,在特定场景下使程序的可读性更强。这种手段在Swift也是相同道理,大家可以善加利用,让自己的代码更加美观。
其实,开发者要想不断精进,成长为真正的大牛高手,必须将自己的视野凌驾于业务需求之上,精简强化核心技能,提升自己对语言和工具的掌握层次,才能提高开发效率,提升技能水平。
这里为你准备了更多好玩的,让你事半功倍的 iOS 高阶黑魔法攻防术,斯达克学院(StuQ )特别邀请备受学员喜爱的资深 iOS 技术专家臧成威老师开设《iOS 实战黑魔法》课程,6周12小时高效 Get iOS 必须掌握的高阶黑魔法攻防术,让你从普通的开发者中渐渐走出来,看到一个不一样的语言,感受不一样的开发!
主讲老师:臧成威,美团·大众点评 iOS 技术专家,QCon 讲师
上课周期:6 周 12 课时系统学习
上课时间:每周五晚21:00-23:00上课
上课形式:zoom 直播视频授课
课程价格:1599 元
报名截止日期:2月10日(周五)18:00
扫描下方二维码或戳阅读原文报名学习课程:
以上是关于如何利用 Objective-C 写一个精美的 DSL ?的主要内容,如果未能解决你的问题,请参考以下文章