摘要:原文链接有大量平均水平左右的工人可被选择参与进来这意味着好招人有成熟的大量的程序库可供选择这意味着大多数项目都是既有程序库的拼装,标准化程度高而定制化场景少开发工具测试工具问题排查工具完善,成熟基本上没有团队愿意在时间紧任务重的项目情况
原文链接:http://pfmiles.github.io/blog/java-groovy-mixed/
有大量平均水平左右的“工人”可被选择、参与进来 —— 这意味着好招人
有成熟的、大量的程序库可供选择 ——
这意味着大多数项目都是既有程序库的拼装,标准化程度高而定制化场景少
开发工具、测试工具、问题排查工具完善,成熟 ——
基本上没有团队愿意在时间紧、任务重的项目情况下去做没有把握的、基础开发工具类的技术试探
有面向对象特性, 适合大型项目开发 ——
无数大型项目已向世人述说,“面向对象”是开发大型软件的优秀代码组织结构
能适应大型团队、多人协作开发 ——
代码需要简单易懂,起码在接口、api层面是这样
—— 这是我所理解的“工业化开发编程语言”的概念
很显然, java就是种典型的“工业语言”, 非常流行,很多企业靠它赚钱,很实际;
但java也是常年被人黑,光是对其开发效率的诟病就已经足够多,不过java始终屹立不倒;
这样的局面其实无所谓高兴还是担忧,理性的程序员有很多种,其中一种是向“钱”看的 —— 我写java代码,就是因为工作需要而已,能帮助我的组织搞定业务,做出项目,这很好;
当有人说java语言不好的时候,理性的程序员不会陷入宗教式的语言战争之中,他会思考这些人说的是否有道理;如果真的发现整个java平台大势已去,他会毫不犹豫地扭头就走,不过直到目前为止,还没有这种迹象出现;
那么,从这些无数次的口水之争中,我们能否从别人的“战场”上发现一些有用的东西, 来改进我们的开发方式,从而使得java这种已经成为一个“平台”的东西走得更远,赚更多的钱呢?
答案是“有的”,感谢那些参与口水战争的、各种阵营的年轻程序员们,有了你们,java speaker们才有了更多的思考;
我就只谈一个最实际的问题:
java被吐槽的这些年, 就开发效率这一点而言,到底有哪些东西是值得借鉴的?也就是说,到底是哪些主要特性直接导致了某些其它语言在语法上相对于java的优越感?
丰富的literal定义在groovy中定义map和list的惯用方式:
def list = [a, 2 ,3] def map = [a:0, b:1]
而java呢?只能先new一个list或map,再一个个add或put进去; 上面这种literal(字面量)形式的写法便捷得多;
而javascript在这方面做得更绝, 我们都用过json,而json其实就是literal形式的object
极端情况下,一门编程语言里的所有数据类型,包括”内建”的和用户自定义的,统统可以写成literal形式;
在这种情形下,其实这种语言连额外的对象序列化、反序列化机制都不需要了 —— 数据的序列化形式就是代码本身, “代码”和“数据”在形式上被统一了
java对这方面几乎没有任何支持,对于提高编码效率来讲,这是值得学习的一点, 起码“内建”数据结构需要literal写法支持
first-class function & higher-order function & function literal(lambda)无论是js, 还是python/ruby,或是groovy,都可以将函数作为另一个函数的参数传入,以便后者根据执行情况判断是否要调用前者
或者能够将一个函数作为另一个函数的返回值返回,以便后续再对其进行调用
这种高阶函数特性,就不要再说java的匿名内部类“能够”实现了, 如果认为匿名内部类已经”够用”了的话,其实就已经与现在的话题“开发效率”相悖了
高阶函数显然是一种值得借鉴的特性,它会让你少写很多很多无聊的“包装”代码;
还有就是匿名函数(lambda)了
我不喜欢lambda、lambda地称呼这个东西,我更喜欢把它叫做“匿名函数”或者“函数字面量(literal)”, 因为它跟数学上的lambda演算还是有本质区别,叫”lambda”有误导的危险
函数字面量的意思就是说,你可以在任何地方,甚至另一个函数体的调用实参或内部,随时随地地定义另一个新的函数
这种定义函数的形式,除了“这个函数我只想在这里用一次,所以没必要给它起个名字”这种理由之外,还有一个更重要的理由就是“闭包”了
所谓闭包,其实也是一个函数,但是在这个函数被定义时,其内部所出现的所有”自由变量(即未出现在该函数的参数列表中的变量)”已被当前外层上下文给确定下来了(lexical), 这时候,这个函数拥有的东西不仅仅是一套代码逻辑,还带有被确定下来的、包含那些“自由变量”的一个上下文, 这样这个函数就成为了一个闭包
那么闭包这种东西有什么好呢?其实如果懒散而钻牛角尖地想,闭包的所有能力,是严格地小于等于一个普通的java对象的,也就是说,凡是可以用一个闭包实现的功能,就一定可以通过传入一个对象来实现,但反过来却不行 —— 因为闭包只有一套函数逻辑,而对象可以有很多套,其次很多语言实现的闭包其内部上下文不可变但对象内部属性可变
既然这样,java还要闭包这种东西来干嘛?其实这就又陷入了”匿名内部类可以实现高阶函数”的困境里了 —— 如果我在需要一个闭包的时候,都可以通过定义一个接口再传入一个对象来实现的话,这根本就跟今天的话题“开发效率”背道而驰了
显然,java是需要闭包的
强大而复杂的静态类型系统这和开发效率有关么?
编程语言不是越“动态”,开发效率越高么?还需要强大而复杂的静态类型系统么?
试想一下这种api定义:
def eat(foo) { ... }
这里面你认识的东西可能只有’吃’了, 你知道foo是什么么?你知道它想吃什么么?吃完后要不要产出点什么东西? —— 你什么都不知道
这种api极易调用出错,这就好比我去买饭,问你想吃什么你说“随便”,但买回肯德基你却说你实际想吃的是麦当劳一样
可能你还会反驳说,不是还有文档么?你把文档写好点不就行了么? —— 不要逼我再提“匿名内部类”的例子,如果给每个函数写上复杂详尽的文档是个好办法,那就显然 —— again, 与“开发效率”背道而驰了
那么,静态类型系统,这里显然就该用上了
静态类型系统在多人协作开发、甚至团队、组织间协作开发是非常有意义的;
拥有静态类型系统的编程语言通常都有强大的、带语法提示功能的IDE,这很正常,因为静态类型语言的语法提示功能好做;
只要把别人的库拿过来,导入IDE,各种函数签名只需扫一眼 —— 很多情况下根本不需要仔细看文档 —— 就已经知道这个函数是干嘛用的了, 合作效率成倍提升;
而且,作为”api”,作为“模块边界”,作为与其它程序员合作的“门面”, 函数签名上能将参数和返回值类型“卡”得越紧越好 —— 这样别人不用猜你这个函数需要传入什么类型,甚至他在IDE里一“点”,这里就给自动填上了 :)
要做到“卡得紧”,光有静态类型系统还不够,这个系统还需强大, 试想一下这个例子:
/** * 我只吃香蕉和猪肉,请勿投食其它物品 */ public void eat(List
这段纯java代码已经是“定义精确”的静态类型了
但如果没有上面那行注释,你很可能会被System err.无数次
而这行注释之所以是必需的,完全是因为我找不到一个比List更好的表达“香蕉或猪肉”的形式, 这种情形足以让人开始想念haskell的either monad
在“强大而复杂的类型系统”这一点上,jvm平台上令人瞩目的当属scala了,可惜java没有,这是值得借鉴的
不过这一点的“借鉴”还需java的compiler team发力,我等也只是说说(按照java保守的改进速度,估计HM类型系统是指望不上了)
动态类型系统,duck-typing刚说完静态类型,现在又来说动态类型系统合适么?
然而这与节操无关,我想表达的是,只要是有助于“开发效率”的,都能够借鉴,这是一个理性的java speaker的基本素质
我们在开发项目的时候,大量的编码发生在“函数”或“方法”的内部 —— 这就好比你在屋子里、在家里宅着一样, 是不是应该少一些拘束,多一些直截了当?
在这种情形下,动态类型系统要不要太爽? ——
Void visitAssert(AssertTree node, Void arg1) { def ahooks = this.hooks[VisitAssertHook.class] ahooks.each {it.beforeVisitCondition(node, errMsgs, this.ctx, resolveRowAndCol, setError)} scan((Tree)node.getCondition(), arg1); ahooks.each {it.afterVisitConditionAndBeforeDetail(node, errMsgs, this.ctx, resolveRowAndCol, setError)} scan((Tree)node.getDetail(), arg1); ahooks.each {it.afterVisitDetail(node, errMsgs, this.ctx, resolveRowAndCol, setError)} return null; }
你知道ahooks是什么类型么?你不知道但我(我是编码的人)知道
你知道ahooks身上有些什么方法可以调么?你同样不知道但我知道
你不知道没关系,只要我知道就行了,因为现在是我在写这段代码;
这段代码写完以后,我只会把Void visitAssert(AssertTree node, Void arg1)这个类型明确的方法签名提供给你调用,我并不会给你看函数体里面的那坨东西,因此你知不知道上面这些真的没关系
方法内部满是def, 不用书写繁复的List这种反人类反社会标语, 每个对象我知道它们身上能“点”出些什么来,我只管“点”,跑起来之后invokedynamic会为我搞定一切
动态类型系统 —— 这就是方法内部实现应该有的样子
哪怕你的方法内部实现就是一坨shi,你也希望这坨shi能尽可能小只一点,这样看起来更清爽是吧?
不要说我太分裂,我要笑你看不穿 —— 静态类型和动态类型既然都有好处,那么他们能放在一起么?
能的,这里就需要点明这篇文章的政治目的了: “java与groovy混编”
而且,目前来看,jvm平台上,只有它二者的结合,才能完成动态静态混编的任务
曾经我发出过这样一段感叹:
公共api、对外接口声明、应用程序边界…这些对外的“脸面”部分代码,如果拥有scala般强大的类型系统…就好了;而私有代码、内部实现、各种内部算法、逻辑,如果拥有groovy般的动态、简单的类型系统…就好了;综上,如果有门语言,在接口和实现层面分别持有上述特性,就好了
这种“理想”中的语言或许某天我有空了会考虑实现一个
而现在,虽说不是scala,但我终于想要在java和groovy身上来试验一把这种开发方式了
这里我坦白一下为什么没用scala,原因很简单,我在技术选型方面是势利的,scala还不被大多数平均水平的java开发人员(参见”工业化开发编程语言”定义第一条)接受,这直接导致项目的推进会遇到困难
而相对来讲,我暂且相信大多数java开发人员都还算愿意跨出groovy这一小步,当然这还需要时间证明
好了,下面还剩下一点点无关痛痒的牢骚 ——
元编程能力macro, eval, 编译过程切入, 甚至method missing机制,这些都算“元编程”
元编程能力的强弱直接决定了使用这种语言创作“内部DSL”的能力
java在元编程方面的能力,几乎为0
这是值得借鉴的
与groovy的混编,顺便也能把groovy的元编程也带进来
各种奇巧的语法糖语法糖,关起门来吃最美味,这也是一种使得“方法内部实现更敏捷”的附加手段
网上随便下载一份groovy的cheat sheet, 都会列举groovy的那些写代码方面的奇技淫巧
这些奇技淫巧,在各种脚本语言之间其实都大同小异, 因为他们本来就是抄来抄去的
结合方法内部的动态类型环境,这一定会进一步缩小方法内部实现代码的体积
我不去讨论什么语言才是The True Heir of Java, 那会使这篇文章变成一封战书,我只关心如何更好地利用现有开发资源完成项目,高效地帮组织实现利益
所以说java和groovy的混编是一种最“势利”的折衷,我不想强迫平均水平的开发人员去学习一种完全不同的语言,短期内不会对项目有任何好处,真正想去学的人他自己会找时间去学
而groovy,说它是java++也不为过,因为java代码直接就可以被groovy编译, groovy完全兼容java语法, 对一般java开发人员来说,这真是太亲切了
这里我要提一下我对“java和groovy混编”的一个个人性质的小尝试 —— kan-java项目
kan-java这个小工具,凡是用户在编码使用过程中能“碰”到的类和接口,全部都由java定义, 这确保用户拿到的东西都有精确的类型定义
凡是对上述接口的实现,都以groovy代码的形式存在
这贯彻了”接口静态类型,内部实现动态类型”的宗旨, 或者说“凡是要提供给另外一个人看、调用的地方(接口或接口类),使用java,否则就用groovy”
当然了,单元测试也完全由groovy代码实现
将kan-java的jar包引入到项目中使用时,就跟使用其它任何纯java实现的jar包一样 —— 接口清晰,参数类型明确,返回类型明确, 你不会也没有必要知道开发人员在具体实现的时候,使用动态语言爽过一把
对于java和groovy的混编,项目的pom.xml如何配置,除了可以参考kan-java的配置外,还可以参考这个gist: https://gist.github.com/pfmiles/2f2ab77f06d48384f113, 里面举例了两种配置方式,各有特色
具体的效果,还需要真正地去实际项目中体会
另外,kan-java也是一个有趣的工具,这个工具所实现的功能我也是从未见到java世界内有其它地方讨论过的,它可以辅助java做“内部DSL”,有场景的可以一试
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64295.html
摘要:软件架构师需要掌握的开源工具的是当前敏捷开发领域最重要的工具之一。主要用于持续自动地构建测试软件项目,如与。监控一些定时执行的任务。是应用程序最好的软件测试框架之一。是一种自由和开放源码的类操作系统 Java软件架构师需要掌握的开源工具1. JIRAAtlassian的JIRA是当前敏捷开发领域最重要的工具之一。它用于错误...
摘要:时间年月日星期二说明本文部分内容均来自慕课网。项目一个项目代表一个正在构建的组件比如一个文件,当构建启动后,会基于实例化一个类,并且能够通过变量使其隐式可用。任务动作定义了一个最小的工作单元。 时间:2017年05月16日星期二说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:无个人学习源码:https://github.com/zccod...
摘要:我们的目标是建立对每一种语言的认识,它们是如何进化的,未来将走向何方。有点的味道是坚持使用动态类型,但唯一还收到合理拥泵的编程语言,然而一些在企业的大型团队中工作的开发者择认为这会是的一个缺陷。 为什么我们需要如此多的JVM语言? 在2013年你可以有50中JVM语言的选择来用于你的下一个项目。尽管你可以说出一大打的名字,你会准备为你的下一个项目选择一种新的JVM语言么? 如今借助来自...
摘要:容器概述接口表示容器,负责实例化配置和组装。基于的元数据不是惟一允许的配置元数据形式,容器本身与实际编写配置元数据的格式完全解耦,现在,许多开发人员为他们的应用程序选择基于的配置。 容器概述 org.springframework.context.ApplicationContext接口表示Spring IoC容器,负责实例化、配置和组装bean。容器通过读取配置元数据获取关于要实例化...
阅读 1734·2023-04-25 19:37
阅读 1297·2021-11-16 11:45
阅读 2801·2021-10-18 13:30
阅读 2762·2021-09-29 09:34
阅读 1613·2019-08-30 15:55
阅读 3110·2019-08-30 11:10
阅读 1833·2019-08-29 16:52
阅读 993·2019-08-29 13:18