知道 Python 语言的 Google Fire 项目么,我将它移植到了 Java 上 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
shellquery
V2EX    程序员

知道 Python 语言的 Google Fire 项目么,我将它移植到了 Java 上

  •  
  •   shellquery 2018-06-11 09:52:59 +08:00 2324 次点击
    这是一个创建于 2746 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近尝试了 Python 语言的开源命令行便捷工具库 Google Fire,它是用来加速用户编写命令行程序的一个小工具库,该工具使用非常方便,节省了编写命令行程序繁琐的参数解析代码的时间。

    巧用 Google Fire 简化 Python 命令行程序

    但是我发现 Java 语言还缺少这样一个工具库,于是花了 2 天时间,将 Google Fire 移植到了 Java 语言上,命名为 Startup。有了 Startup,Java 同学也可以从此不用解析繁琐的命令行参数了。

    Startup 项目的 Github 代码地址

    安装依赖

    <dependency> <groupId>com.github.pyloque</groupId> <artifactId>startup</artifactId> <version>0.0.1</version> </dependency> 

    让 Jedis 秒变命令行

    import java.io.IOException; import codehole.startup.Startup; import redis.clients.jedis.Jedis; public class Demo { public static void main(String[] args) { Startup.shell(new Jedis()); } } 

    使用 maven 将程序打包成 jar 包文件 demo.jar ,接下来就可以体验命令行版本的 Jedis 了。

    $ java -jar demo.jar > @set codehole superhero OK > @get codehole superhero > @keys * [codehole] > @del codehole 1 > @sadd books str[]:java,golang,python 1 > @sadd books str[]:java,golang,python 3 > @smembers books [python, golang, java] > @scard books 3 

    我们还可以使用单行命令模式,将 shell 方法改成 fire 即可,有了 fire 函数,繁琐的命令行参数解析过程就可以彻底拜拜了。

    Startup.fire(new Jedis(), args); // 需要传递 args 参数 

    接下来,我们尝试一下单行命令的效果

    $ java -jar demo.jar @set codehole superhero OK $ java -jar demo.jar @get codehole superhero $ java -jar demo.jar @keys "*" [codehole] $ java -jar demo.jar @del codehole 1 $ java -jar demo.jar @sadd books str[]:java,golang,python 3 $ java -jar demo.jar @smembers books [python, golang, java] $ java -jar demo.jar @scard books 3 

    Redis 的 set 命令在 Jedis 里存在多个重载函数,很难记清楚具体有哪些参数。不过没关系,Startup 提供了类自省功能,可以列出指定匹配模式的函数调用形式。

    $ java -jar demo.jar ! @set class: redis.clients.jedis.Jedis method: set(String arg0, String arg1, String arg2) method: set(String arg0, String arg1, String arg2, String arg3, int arg4) method: set(String arg0, String arg1) method: set(String arg0, String arg1, String arg2, String arg3, long arg4) class: redis.clients.jedis.BinaryJedis method: set(byte[] arg0, byte[] arg1, byte[] arg2) method: set(byte[] arg0, byte[] arg1, byte[] arg2, byte[] arg3, int arg4) method: set(byte[] arg0, byte[] arg1, byte[] arg2, byte[] arg3, long arg4) method: set(byte[] arg0, byte[] arg1) 

    自省操作符是!,它连带祖先类一会自省了。当 set 方法有如此多时,如何调用具体哪个 set 方法呢? Startup 使用参数数量和参数类型来匹配函数,如果有多个函数满足匹配条件,就使用第一个函数。比如现在要调用 byte[]类型的 set 函数,我们可以这样写

    > java -jar demo.jar @set byte[]:1,2,3 byte[]:10,20 OK > java -jar demo.jar @get byte[]:1,2,3 [B@ae45eb6 

    因为输出是一个 byte[]数组,所以显示的是它的 toString()调用的结果。如果用户不满意输出效果,可以通过自定义IConsole.print方法来优化输出。

    Startup.fire(Object target, String[] args, IConsole console); 

    细节规则

    1. 方法调用使用 @前缀,如 @set
    2. 字段访问使用$前缀,如$host
    3. 方法调用如果遇到同样参数数量的方法不止一个,那么在传递参数时就必须携带参数类型前缀信息,便于 startup 确定具体应该调用哪个方法。如@set str:codehole str:superhero,表示调用两个参数都是 String 类型的 set 方法。如果不给予参数类型提示,那么自动使用第一个找到的方法,这可能不是你想要的。
    4. startup 的 target 参数可以是普通对象,也可以是类。比如Startup.fire(Jedis.class, args),那么后续的参数作用对象就是 Jedis.class。
    5. 如果 target 是类,那么可以使用操作符+号对类进行实例化,后面跟着构造器的参数列表。比如 Jedis 有构造器Jedis(String host, int port),那么就可以这样实例化+ str:localhost int:6379
    6. 如果 target 是类,并且有默认构造器的话,那么如果要访问实例方法或者实例字段的话,就会对这个类进行自动实例化。如果没有默认构造器,这时候就会抛出异常。
    7. 如果某个方法返回值为 void,Startup 会进行特殊处理,返回调用对象自身。
    8. 自省操作符!可以使用! @显示所有方法,可以使用! $显示所有字段,如果不带参数,等价于! *列出所有的字段和方法。像 Jedis 类里面实例方法特别多,如果全部列出来会让人崩溃,这时可以用第三个参数 maxlen,表示最多显示多少行。比如! @ 100最多显示 100 行方法。还可以通过模式匹配*号来查找方法,比如! @*set*匹配所有包行 set 字符串的方法列表。
    9. 输入 exit 和 q 可以退出命令行 shell。

    实例演示

    下面我们使用链式调用的例子来演示 Startup 的强大威力。首先我们编写一个 Counter 类,可以对该类里面的整形字段 value 进行 incr 和 decr 操作。然后使用 Startup 对它进行 shell 化。同时我们定一个 coeff 静态变量,用于放大自增自减的效果。

    import codehole.startup.Startup; public class Demo { static class Counter { int value; static int coeff = 1; public Counter() { this(10); } public Counter(int value) { this.value = value; } public static int coeff() { return coeff; } public static void coeff(int c) { coeff = c; } public Counter incr() { return incr(1); } public Counter incr(int v) { this.value += v * coeff; return this; } public Counter decr() { return incr(1); } public Counter decr(int v) { this.value -= v * coeff; return this; } public int value() { return value; } public String toString() { return String.format("Counter(value=%d)", value); } } public static void main(String[] args) { Startup.shell(Counter.class); // 注意,这里的目标是类 } } 

    我们运行一下,体验一下 Startup 的神奇魅力。

    自省

    $ java -jar demo.jar # 列出所有方法 > ! @ class: codehole.startup.demo.Demo.Counter method: value() method: decr() method: decr(int arg0) method: static coeff(int arg0) method: static coeff() method: incr(int arg0) method: incr() # 列出所有字段 > ! $ class: codehole.startup.demo.Demo.Counter field: int value field: static int coeff # 列出所有的 incr 方法 > ! @incr class: codehole.startup.demo.Demo.Counter method: incr(int arg0) method: incr() # 列出所有的 decr 方法 > ! @decr class: codehole.startup.demo.Demo.Counter method: decr() method: decr(int arg0) # 列出后缀为 cr 的方法 > ! @*cr class: codehole.startup.demo.Demo.Counter method: decr() method: decr(int arg0) method: incr(int arg0) method: incr() 

    构造器

    # 默认构造器 > + Counter(value=10) # 调用构造器 Counter(int) > + 5 Counter(value=5) # 构造完继续获取实例字段 > + $value 10 > + 5 $value 5 # 构造完继续调用实例方法 > + @value 10 > + 5 @value 5 # 对类[访问实例字段或调用实例方法]会自动构造该类的默认实例,再回调方法或者访问实例字段 > $value 10 > @value 10 

    静态字段和方法

    # 对类调用静态方法和对实例调用静态方法效果是一样的 > @coeff 1 > + @coeff 1 # 对类访问静态字段和对实例访问静态字段效果是一样的 > $coeff 1 > + $coeff 1 # 返回类型为 void 的方法会自动调整返回结果为调用对象 > @coeff 2 class codehole.startup.demo.Demo$Counter > + @coeff 2 Counter(value=10) > @coeff 2 $coeff 2 

    链式调用

    > @incr @incr @incr 5 @incr 5 $value 22 > @incr 5 @decr 5 @incr 5 @decr 5 $value 10 > + @incr @incr @incr 5 @incr 5 $value 22 > + @incr 5 @decr 5 @incr 5 @decr 5 $value 10 

    总结

    Java 版本的 Startup 和 Google Fire 使用上还是有一些不一样,这个是语言的机制差别所致。但是使用上是大同小异,基本上 Google Fire 能做到的事,Startup 都能做了,只是在使用形式上略有不同。

    Startup 继承了 Fire 的优良传统,小巧轻便没有任何依赖项,使用一行代码就把命令行的繁琐解析过程给彻底消灭了。同时还提供了 Fire 所欠缺的自省功能和交互式 shell,用户使用 startup 进行调试工作时会无比轻松。

    阅读更多高级文章,关注微信订阅号「码洞」

    tysx
        1
    tysx  
       2018-06-11 12:47:46 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3893 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 05:27 PVG 13:27 LAX 21:27 JFK 00:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86