锁定老帖子 主题:付出太多,得到太少 - 闲谈函数返回值
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-09-07
理论上来说,对象是否应该具备自杀的能力?。
模仿这个:把技术话题放海版,扯起来轻松一点。 我的问题是:函数返回值太单一。 一个函数的名字表明了它的功能,它的输入可以很灵活,多个参数,参数可以包含复杂的数据结构,有的语言还支持参数缺省值、keyword形式,重载还能在不改变函数名的前提下让输入更灵活,this/全局变量其实也算输入。 返回值的形式就太单一了,只能return固定类型的单个值。 举个例子。 给用户分配任务:user.assignTask(task) (一)我希望可以这样用: boolean success = user.assignTask(task); if(success){ .... }else{ display("Task assignment failed"); } (二)还能这样用: AssignResult result = user.assignTask(task); if(result.success){ .... }else{ display(result.errors); } (三)还能这样: try{ user.assignTask(task); .... }catch(AssignException ex){ display(ex.errors); } (四)还能: user.assignTask(task1).assignTask(task2); 假定assignTask的内部逻辑很复杂。 讨论不限java。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-09-07
引用 返回值的形式就太单一了,只能return固定类型的单个值。 那是静态类型语言才存在的 比如Ruby的返回值就不需要是固定类型的 但无论如何你需要“知道”什么将被返回 所以,我不明白为什么你“想要”这些东西 |
|
返回顶楼 | |
发表时间:2008-09-07
即使是静态语言,也可以返回 数组,集合,HashMap. 想返回啥,就返回啥。 |
|
返回顶楼 | |
发表时间:2008-09-07
#include<iostream>
using namespace std; #define boolean int class AssignException{ public: int errors; }; class AssignResult{ public: int n; }; class u{ public: int n; operator boolean (){return n;} operator AssignResult(){AssignResult a;a.n=n;return a;} u assignTask(int task); }; u u::assignTask(int task) { u uu; AssignException a; a.errors=1111; if(task==0)throw a; uu.n=2222; return uu; } int main() { u uu,uu2; AssignResult a; boolean b; a=uu.assignTask(1); cout<<a.n<<endl; b=uu.assignTask(1); cout<<b<<endl; uu2=uu.assignTask(1).assignTask(2); cout<<uu2.n<<endl; try{ uu.assignTask(0); }catch(AssignException ex){ cout << ex.errors<<endl; } return 0; } |
|
返回顶楼 | |
发表时间:2008-09-07
Object[] assignTask(Task);
V assignTask(T) |
|
返回顶楼 | |
发表时间:2008-09-08
这是砣伪需求,鉴定完毕
|
|
返回顶楼 | |
发表时间:2008-09-08
gigix 写道 但无论如何你需要“知道”什么将被返回
所以,我不明白为什么你“想要”这些东西 返回了true/false,就不能返回更详细的信息,也不能返回taskId,或返回user。 返回了数组,就不能把它直接当成taskId用。 抛出了异常,就不能返回值。 这就是问题。 对前面的用法编了号。 再加一个(用法五): int taskId = user.addTask(task); 得到新创建的taskId。 解释一下用法四和其它用法的区别: 只有调用者用try catch包起来时,出错才会抛异常。否则,出错只是普通返回。 ruby对用法四的惯例是 user.add_task!(task) 这样,出错后,不返回true/false,而是raise Exception。 这本质是加了名为"add_task!"的另一个函数,并没有从语言层面入手。 对这个惯例的解读:调用方不但说明了被调函数的语意(add_task),同时说明了调用者的意图和使用方式(!号)。 技术上,我们用参数、this、重载、多态等方式,把输入的多样性充分的拆解了。然而,输出的多样性呢? 这个惯例,隐含了如下信息:在函数的数据输出和处理方式上,是多样的,也有一定的模式可循。如我前面列出的,或者判断成功失败,或者取操作详细信息,或者捕获异常,或者直接使用返回的业务数据,或者链式调用。 相信很多人在设计系统接口时,对采用哪种“输出模式”头痛吧? 在我看来,异常,是为了弥补返回值类型单一和用法单一的问题,引入的另一种返回值形式。参考java的checked exception在方法签名上的表现。 以这个思路,用法一和用法二,可以约定这样调用 user.assign_task?(task) assign_task?返回一个通用的Result实例,并象ddd给出的例子,让它能够“类型协变” - 可能用错词了,总之能把它当作boolean使用,也能从中取到errors等信息。 再进一步,比如,add_task的内部实现里,一些关键性操作产生的中间数据,能不能像errors一样,以“可选返回值”的方式提供出来?(既然有可选输入参数) ruby/python中,返回数组,再用平行赋值的方式接收,部分解决了这个问题。但同时改了“主返回值”类型(改成了数组),不完美。 这些工作,能不能再深入一点,在语言层做呢? 只定义一次add_task,就有了add_task!和add_task?(这一点,动态语言很接近了) 把Result类型作为语言层面的东西,自动封装函数返回值,或者直接作为Object的一个特性,可以类型协变成“主返回值”,支持“可选返回值”,响应!?等调用者抽象意图。 看前面五种用法,只有用法五的返回值代表了业务数据(taskId,作为主返回值),其它四种都是对操作过程的描述(操作成败,错误信息,作为可选返回值)。 对于静态语言,能不能利用编译器,实现“返回值重载”呢?(既然有参数重载) 根据接收变量的类型,返回正确的值。让这两行都好使: boolean success = user.addTask(task); int taskId = user.addTask(task); 底下的事,编译器帮咱干了!~ 咱这叫"result pattern match",或"result type inferred" 我所了解的type inferred实现,只是承认并接受了返回值信息不够丰富的事实,并没有试图打破它。 有没有可能两者结合? 关于用法四的链式调用,多数语言是靠设定返回值为this实现的(return this)。这样就不能返回其它东西了。 能不能另辟蹊径,比如通过其它操作符: user->assignTask(task1)->assignTask(task2); 再比如OGNL Chained Subexpressions的形式: user.(assignTask(task1),assignTask(task2)) 参考: http://www.ognl.org/2.6.9/Documentation/html/LanguageGuide/chainedSubexpressions.html 我爱OGNL~~~ 输出数据不丰富,也导致很多人用实例变量进行中转。--如果不遵循Command等模式的潜在约定,就会引发逻辑问题,或使代码的可维护性变差。 总结一下: 1。 输出数据 范式。 2。 调用者处理方式 范式。 俺这是信马由缰的头脑风暴,所以丢到了海版。在默念master乌龟的话: 引用 Quit? Don't quit? Noodles? Don't noodles? 在现有语言设施上,怎么处理这些问题呢? |
|
返回顶楼 | |
发表时间:2008-09-08
这个问题8素函数的问题,也8素静态类型的问题
而素静态OO的问题. 若不考虑OO,静态类型语言只要Type inference就可以搞定鸟... 但素,静态OO里方法是类滴成员.方法可以素虚滴,或者像Java那样所有的方法都素虚滴.因此如果出现了虚函数的继承问题,就必须在运行期解决。所以如果在编译器做result interfernce滴话,虚函数就完蛋鸟. |
|
返回顶楼 | |
发表时间:2008-09-08
异常状态代码 方法名(输入参数1,输入参数2,(。。。。。多个)侦听参数,输出参数1,输出参数2(。。。。多个)); 这样子设计方法么? |
|
返回顶楼 | |
发表时间:2008-09-08
直接上代码~
没有考虑NilClass,FixNum; 错误重置做的很粗糙: class Object def carry(hash={}) reset_carriage metaclass = (class << self; self; end) metaclass.send(:define_method,:success?) do hash.empty? end metaclass.send(:define_method,:errors) do hash[:errors] end self end def reset_carriage metaclass = (class << self; self; end) if self.respond_to?('errors') # @_@ metaclass.send(:remove_method, :success?) metaclass.send(:remove_method, :errors) end self end def success? true end end class Class def exclamation(*methods) methods.each do |m| self.send(:define_method,"#{m}!") do |*args| result = self.send(m,*args) if result.success? return result else raise Exception.new(result.errors) end end end end end class Task attr_accessor :name def initialize(name) @name = name end end class User attr_accessor :tasks def initialize @tasks = [] end def assign_task(task_name) if task_name == 'critical task' return nil.carry(:errors => "can't assign critical task to him!") end task = Task.new(task_name) @tasks << task return task end exclamation :assign_task end user = User.new task1 = user.assign_task("task1") puts task1 # #<Task:0xb7df9b54>(newly created instance) puts task1.success? # true puts '-------' critical_task = user.assign_task("critical task") puts critical_task # nil puts critical_task.success? # false puts critical_task.errors # can't assign critical task to him! puts '--------' task2 = user.assign_task!("task2") puts task2 # #<Task:0xb7df98d4>(newly created instance) puts task2.success? # true puts '-------' critical_task = user.assign_task!("critical task") # will raise Exception |
|
返回顶楼 | |