扫描二维码关注官方公众号
返回列表
+ 发新帖
查看: 117|回复: 0

[转载发布] 【闲谈】【ruby|JavaScript】更富表现力的接口编写

[复制链接]
累计送礼:
0 个
累计收礼:
0 个
  • TA的每日心情
    开心
    2025-2-4 02:05
  • 签到天数: 110 天

    连续签到: 2 天

    [LV.6]常住居民II

    2327

    主题

    395

    回帖

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    VIP
    6
    卡币
    10465
    OK点
    16
    推广点
    0
    同能卷
    0
    积分
    13215

    灌水之王

    发表于 2024-2-16 09:07:16 | 显示全部楼层 |阅读模式
    更新:修改了一楼提到的错误


    在编写脚本,尤其是在做一些底层库的时候,接口实现是很重要的一环。而方便调用的接口对于使用者来说亦是非常关键。而这篇帖子,就是探讨接口实现这一话题。

    先看一个例子:
    想想看,我们创建并显示一个精灵(sprite)的时候要经过几个步骤?
    RUBY 代码
    1. sprite = Sprite.new()
    2. sprite.bitmap = bitmap
    3. #一般创建之后还要调整坐标之类的
    4. sprite.x = 12
    5. sprite.y = 13
    6. sprite.z = 100
    复制代码

    如果是MV会麻烦些:
    JAVASCRIPT 代码
    1. ClassName.prototype.method.function(){
    2. //一些其他部分的代码
    3. this.sprite = new Sprite(bitmap);
    4. this.sprite.x = 12;
    5. this.sprite.y = 13;
    6. this.addchid(this.sprite);
    7. //一些其他代码
    8. }
    复制代码

    在创建sprite的时候,要调整的属性和要调用的方法并不是很多,如果是一些需要在创建后进行大量调整的对象,整个过程会变得很麻烦。【想想那一堆需要打的sprite】
    有一个办法可以让你的接口在调用的时候更方便。这种方法被称为级联(cascadeJavaScript语言精粹  Douglas Crockford )。

    级联
    级联多用于一些进行属性调整或者修改对象的某个状态。这样的原因是这些方法一般都没有返回值。级联的关键是让这些方法返回this(ruby中就是self)从而在一条语句中一次调用同一个对象的多个方法。
    JAVASCRIPT 代码
    1. //一个级联的例子
    2. var button = new Button()
    3.   .move(12,23)
    4.   .width(45)
    5.   .height(200)
    6.   .text("开始游戏")
    7.   .on('mousedown', function(){Scene.goto(MapScene)});
    复制代码

    ruby的差不多就不写了。
    要说明的是级联算是js中的一个概念,在node、jQuery里都有应用。不过当你明白他到底是啥后,很快就能在ruby里实现一遍。
    级联的实现
    js中的实现
    JAVASCRIPT 代码
    1. A = function(id){
    2.   this._id = id;
    3.   this._x = 0;
    4.   this._y = 0;
    5.   this.event = {};
    6.   returnthis;
    7. }
    8. A.prototype.constructor = A;
    9. A.prototype.x = function(val){
    10.   if(val === undefined)returnthis._x;
    11.   this._x = val;
    12.   returnthis;
    13. }
    14. A.prototype.y = function(val){
    15.   if(val === undefined)returnthis._y;
    16.   this._y = val;
    17.   returnthis;
    18. }
    19. A.prototype.method1 = function(val){
    20.   this._x = this._x + val;
    21.   returnthis;
    22. }
    23. A.prototype.method2 = function(val){
    24.   this._y = this._y + val;
    25.   returnthis;
    26. }
    27. A.prototype.set_event = function(name, func){
    28.   Object.defineProperty(this.event, name, {value: func})
    29.   returnthis;
    30. }
    31. a = new A(1)
    32.     .x(12)
    33.     .y(13)
    34.     .method1(12)
    35.     .set_event('click', function(){console.log(a.x(),a.y())})
    36.     .method2(12);
    37. a.event['click']()
    复制代码

    ruby中等同效果的实现
    RUBY 代码
    1. class A
    2.   attr_reader :id
    3.   attr_accessor :x,:y
    4.   def initialize(id)
    5.     @id = id
    6.     @x = 0
    7.     @y = 0
    8.     @event = {}
    9.   end
    10.   def x(x = nil)
    11.     return@xif x == nil
    12.     @x = x
    13.     returnself
    14.   end
    15.   def y(y = nil)
    16.     return@yif y == nil
    17.     @y = y
    18.     returnself
    19.   end
    20.   def method1(x)
    21.     @x += x
    22.     self
    23.   end
    24.   def method2(y)
    25.     @y += y
    26.     self
    27.   end
    28.   def event(name = nil, &block)
    29.     return@eventif(name == nil)
    30.     @event[name] = lambda &block
    31.     self
    32.   end
    33. end
    34. a = A.new(1)
    35.   .x(12)
    36.   .y(15)
    37.   .method1(12)
    38.   .method2(13)
    39.   .event(:click){p a.x,a.y}
    40. a.event[:click].call
    复制代码

    这个范例中除了级联外,顺便实现了函数参数和它的调用【js中实现很简单,ruby中大概就是&和lambda,这个后面还会提到】
    在ruby的实现中,每一个参数都要写一个函数来实现接口,不过使用元编程的话,可以实现类似于attr_XXX系列方法的效果。
    RUBY 代码
    1. classClass
    2.   def attr_cascade(*arg)
    3.     arg.eachdo |i|
    4.     attr_accessor i
    5.       define_method(i)do |val = nil|
    6.         return instance_variable_get('@'+i.id2name)if val == nil
    7.         instance_variable_set('@'+i.id2name, val)
    8.         self
    9.       end
    10.     end
    11.   end
    12. end
    复制代码


    其他部分
    除了级联外,要实现好的接口,首先得熟悉函数的带有默认值参数【可缺省参数】、不定参数以及函数参数的编写方法。

    参数默认值:
    在ruby中使用下面的方法:
    RUBY 代码
    1. def foo(arg = nil)
    复制代码

    js中即使函数调用不传参依然不会报错【这个参数的值会被设置为undefined】所以js中只需要判断参数的值是否为undefined,如果是则为缺省,设置默认值。

    不定参数
    不定参数是指传递给函数的参数个数是不确定。例如一个sum的方法,效果是返回传入的所有参数的和。
    ruby:
    RUBY 代码
    1. def sum(*arg)
    2.   sum = 0
    3.   arg.each{|i| sum += i}
    4.   return sum
    5. end
    复制代码

    js:
    JAVASCRIPT 代码
    1. function sum(){
    2.   var sum = 0;
    3.   var  length = arguments.length;
    4.   for(var i = 0; i < length; i++){
    5.     sum += arguments[i];
    6.   }
    7.   return sum;
    8. }
    复制代码

    这里用到了js中的arguments。arguments是js中函数调用时自动传递给函数的一个参数,他是一个函数的实参"数组"。函数可以通过访问这个数组来得到调用是传递给他的参数列表。要注意的是他本身并不是一个真正的数组。而只是一个类似数组的对象。

    函数参数:
    js中函数参数没啥要说的。主要说说ruby。ruby中可以通过定义带&的参数来使得函数可以接受一个block作为参数【要注意的是这样得到的是个proc类的实例,所以这里也会在传递函数的时候把它的运行环境一并传递过去。另外,一个block只能算是代码片段,而不是函数,所以如果要将这个传递过来的函数放入变量中,需要先把他转换为函数。【可以使用lambda来转换为匿名函数】

    总结
    接口设计本质上就是对于用作接口的函数的编写。最基本的就是各种函数的具体实现(带缺省值、不定参数、函数/方法参数)。而级联技术则可以让你的接口调用起来更方便【尤其是对于在设计如GUI之类需要进行大量的属性调整和对象状态调整的系统时尤为重要。】
    但这并不是接口设计的全部,只能说这只是些极为初级的技巧除此之外,在ruby中一些元编程的方法也极为重要【例如最常用到的method_missing】在js中还有回调柯里化等。然而这些也都只是纯粹技术方面的了。然而接口设计最重要的还是思想/范式方面的一些东西。不过我个人才学疏浅,这篇帖子也只能算是抛砖引玉。
                本帖来自P1论坛作者墨凌羽,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=385905  若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
    天天去同能,天天有童年!
    回复 送礼论坛版权

    使用道具 举报

    文明发言,和谐互动
    文明发言,和谐互动
    高级模式
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关闭

    幸运抽奖

    社区每日抽奖来袭,快来试试你是欧皇还是非酋~

    立即查看

    聊天机器人
    Loading...

    QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

    GMT+8, 2025-3-10 15:47 , Processed in 0.133757 second(s), 53 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表