javascript – 组合getter和setter方法有什么好处?

前端之家收集整理的这篇文章主要介绍了javascript – 组合getter和setter方法有什么好处?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我见过一些API,特别是脚本语言(我们在团队中使用Perl和JS),它们使用组合的getter和setter方法.例如,在jQuery中:
  1. //append something to element text
  2. var element = $('div#foo');
  3. element.text(element.text() + 'abc');

例如,在Perl的CGI.pm模块中:

  1. # read URL parameter from request
  2. my $old_value = $cgi->param('foo');
  3. # change value of parameter in request
  4. $cgi->param('foo',$new_value);

Perl代码库中的一些通用DAO类使用类似的模式.自动生成的访问器调用内部getset()方法,类似于:

  1. sub foo { # read/write accessor for the foo() property (autogenerated)
  2. my ($self,$new_value) = @_;
  3. return $self->getset('foo',$new_value);
  4. }
  5.  
  6. sub getset {
  7. my ($self,$field,$new_value) = @_;
  8. ## snip (omitted some magic here) ##
  9.  
  10. # setter mode
  11. if (defined $new_value) {
  12. ## snip (omitted some magic here) ##
  13. $self->{data}{$field} = $new_value;
  14. ## snip (omitted more magic here) ##
  15. }
  16.  
  17. # getter mode
  18. return $self->{data}{$field} // '';
  19. }

我看到这个设计存在多个问题:

> Setter调用也通过getter代码路径,这是低效的,特别是如果你有关系,所以getter必须将存储的ID解析为一个对象:

  1. $foo->bar($new_bar); # retrieves a Bar instance which is not used

> getter调用需要携带$new_value参数.当您经常调用Perl方法时(例如,100000次,报告和其他cronjobs中的常规记录数),这可能已经导致可测量的开销.
> Setter不能采用未定义的值. (一个Perl特定问题,我可以通过检查参数计数而不是参数defined-ness来解决这个问题,但是这会使自动生成的访问器更加复杂,并且可能会破坏一些手动访问器.)
>降低grepability:如果我有单独的getter和setter(例如每个foo属性的foo()和set_foo()),我可以使用简单的grep搜索setter调用.
>甚至更多?

我想知道组合设置和获取方法是否有任何实际好处,或者它是否是各个语言/图书馆社区中的一种奇怪传统.

解决方法

概要

>拥有独立的getter / setter或具有组合访问器是一种文化偏好.没有什么可以阻止你选择较少的路径.
>你的大多数缺点都不存在.不要将实现细节与风格决策的问题混淆.

使用不同的set_和get_方法看起来是自我记录的,但是

>如果不发生自动生成,那么写起来很痛苦,并且
>主要是静态访问控制的一种形式:我可以在不暴露set_的情况下获得get_.

在使用细粒度访问控制和许可系统的情况下,后一点尤为重要.在Java,C#,C等语言中,可见的细微差别如私有/受保护/公共等等.由于这些语言有方法重载,编写统一的getter / setter并非不可能,而是文化偏好.

从我个人的角度来看,统一访问者具有这些优势

> API更小,可以使文档和维护更容易.
>根据命名约定,每次方法调用可减少3-4次击键.
>对象感觉更像结构.
>我可以想到没有真正的缺点.

我个人认为,foo.bar(foo.bar()42)在眼睛上比foo.setBar(foo.getBar()42)更容易.但是,后一个例子清楚地说明了每种方法的作用.统一访问器使用不同语义重载方法.我认为这感觉很自然,但它显然使理解代码片段变得复杂.

现在让我分析你所谓的缺点:

分析组合getter / setter的所谓问题

Setter调用遍历getter代码路径

这不是必须的,而是您正在考虑的实现的属性.在Perl中,你可以合理地写

  1. sub accessor {
  2. my $self = shift;
  3. if (@_) {
  4. # setter mode
  5. return $self->{foo} = shift;
  6. # If you like chaining methods,you could also
  7. # return $self;
  8. } else {
  9. # getter mode
  10. return $self->{foo}
  11. }
  12. }

代码路径是完全独立的,除了真正常见的东西.请注意,这也将在setter模式下接受undef值.

[…]检索未使用的Bar实例

在Perl中,您可以自由地检查调用方法的上下文.您可以拥有在void上下文中执行的代码路径,即抛弃返回值时:

  1. if (not defined wantarray) { be_lazy() }

getter调用需要在可测量的开销周围携带$new_value参数.

同样,这是一个特定于实现的问题.此外,您忽略了这里的实际性能问题:您访问者所做的只是调度方法调用.方法调用很慢.当然,方法解析是缓存的,但这仍然意味着一个哈希查找.这比参数列表中的额外变量贵得多.

请注意,访问器也可以像

  1. sub foo {
  2. my $self = shift;
  3. return $self->getset('foo',@_);
  4. }

摆脱了一半我无法通过undef问题.

Setter不能采用未定义的值.

……这是错的.我已经介绍过了.

Grepability

我将使用grepability的这个定义:

If a source file is searched for the name of a method/variable/…,then the site of declaration can be found.

禁止像愚蠢的自动生成

  1. my @accessors = qw/foo bar/;
  2. for my $field (@accessors) {
  3. make_getter("get_$field");
  4. make_setter("set_$field");
  5. }

在这里,set_foo不会将我们带到声明点(上面的代码片段).但是,自动生成就好

  1. my @accessors = qw/foo bar/;
  2.  
  3. for my $field (@accessors) {
  4. make_accessor($field);
  5. }

确实满足上述grepability的定义.

如果我们喜欢,我们可以使用更严格的定义:

If a source file is searched for the declaration Syntax of a method/variable/…,then the site of declaration can be found. This would mean “function foo” for JS and “sub foo” for Perl.

这只需要在自动生成时,将基本声明语法放入注释中.例如.

  1. # AUTOGENERATE accessors for
  2. # sub set_foo
  3. # sub get_foo
  4. # sub set_bar
  5. # sub get_bar
  6. my @accessors = qw/foo bar/;
  7. ...; # as above

使用组合或单独的getter和setter不会影响grepability,只触及自动生成.

关于非愚蠢的自我生成

我不知道你如何自动生成你的访问器,但产生的结果不必太糟糕.要么使用某种形式的预处理器,在动态语言中感觉很傻,要么使用eval,这在现代语言中会感觉很危险.

在Perl中,我宁愿在编译期间通过符号表破解我的方式.包命名空间只是哈希,其名称如%Foo ::具有所谓的globs作为条目,可以包含coderefs.我们可以访问类似* Foo :: foo的glob(请注意* sigil).所以不要这样做

  1. package Foo;
  2. sub foo { ... }

我也可以

  1. BEGIN {
  2. *{Foo::foo} = sub { ... }
  3. }

现在让我们考虑两个细节:

>我可以关闭严格的’refs’,然后可以动态组合子程序名称.
>那个子……这是一个封闭!

因此,我可以遍历一个字段名称数组,并为它们分配相同的子例程,区别在于每个字段名称关闭不同的字段名称

  1. BEGIN {
  2. my @accessors = qw/foo bar/;
  3. # sub foo
  4. # sub bar
  5.  
  6. for my $field (@accessors) {
  7. no strict 'refs';
  8.  
  9. *{ __PACKAGE__ . '::' . $field } = sub {
  10. # this here is essentially the old `getset`
  11.  
  12. my $self = shift;
  13.  
  14. ## snip (omitted some magic here) ##
  15.  
  16. if (@_) {
  17. # setter mode
  18. my $new_value = shift;
  19. ## snip (omitted some magic here) ##
  20. $self->{data}{$field} = $new_value;
  21. ## snip (omitted more magic here) ##
  22. return $new_value; # or something.
  23. }
  24. else {
  25. # getter mode
  26. return $self->{data}{$field};
  27. }
  28. };
  29. }
  30. }

这比只委托给另一个方法更容易,更有效,并且可以处理undef.

如果维护程序员不知道这种模式,那么缺点是可维护性降低.

此外,来自该子内部的错误报告为来自__ANON__:

  1. Some error at script.pl line 12
  2. Foo::__ANON__(1,2,3) called at Foo.pm line 123

如果这是一个问题(即访问者包含复杂的代码),可以通过使用Sub :: Name来减轻这种情况,正如Stefan Majewsky在下面的评论中指出的那样.

猜你在找的JavaScript相关文章