2014-08-29 106 views
4

在Ruby中定义访问者时,可能会在简洁(我们都爱)和最佳实践之间产生紧张关系。Ruby私有和公共访问者

例如,如果我想揭露上的一个实例的值,而是从更新它禁止任何身外之物,我能做到以下几点:

class Pancake 
    attr_reader :has_sauce 

    def initialize(toppings) 
    sauces = [:maple, :butterscotch] 
    @has_sauce = toppings.size != (toppings - sauces).size 
... 

但是突然我使用的是原始实例变量,这让我抽搐。我的意思是,如果在未来的日期设置之前需要处理has_sauce,我可能需要做更多的重构,而不仅仅是覆盖访问器。来吧,原始实例变量?布莱什。

我可以忽略该问题并使用attr_accessor。我的意思是,任何人都可以设置属性,如果他们真的想要;毕竟,这是Ruby。但是后来我失去了数据封装的想法,对象的接口定义不好,系统可能更加混乱。

另一种解决方案是定义在不同的访问修饰符一对访问器:

class Pancake 
    attr_reader :has_sauce 
    private 
    attr_writer :has_sauce 
    public 

    def initialize(toppings) 
    sauces = [:maple, :butterscotch] 
    self.has_sauce = toppings.size != (toppings - sauces).size 
    end 
end 

哪干得不错,但是这是样板的一个简单的访问,并坦率地说,一大块:EW。

那么是否还有更好的Ruby方法?

+0

**哪些工作可以完成**错!你的initialize()方法中的has_sauce是一个局部变量 - 不是一个实例变量。你甚至没有测试你的代码(它也有另一个错误)。 **我的意思是,如果我在未来的日期设置之前需要处理has_sauce,那么我可能需要做更多的重构,而不是重写访问器。**通过设置setter来设置实例变量是一种很好的做法,并且使用两个访问修饰符是实现只读实例变量的方法。 – 7stud 2014-08-29 17:45:26

+0

未经测试的代码最深切的歉意,并很好的发现。现在修复,fwiw。另外,我很高兴你同意这是一个很好的做法,但是这种实现感觉有点不好意思。可能只是我。 – 2014-09-02 09:49:39

回答

3

attr_reader等只是方法 - 没有理由,你可以定义变量为自己使用(和我分享你的看法)例如:

class << Object 
    def private_accessor(*names) 
    names.each do |name| 
     attr_accessor name 
     private "#{name}=" 
    end 
    end 
end 

然后使用private_accessor,你会attr_accessor(我想你虽然需要比private_accessor更好的名字)

+0

这更像它!我正在考虑这样做,但我想在更改对象之前探索所有其他选项......我倾向于尝试不干扰语言的基本原则,除非它非常冒险和/或效率不高。或者除非我真的很想。 ;) – 2014-09-02 09:53:43

+0

另外,我无法想象访问者的好名字!也许这就是为什么它不在语言中。我确实在我的旅行中找到了这个:https://github.com/dbrady/scrapbin/blob/master/scraps/scoped_attr_accessor.rb,它具有大型测试套件的优点,但缺点是需要两个访问器来组成要求的结果。 – 2014-09-02 09:57:26

+0

**和我分享你的感受**真的吗?因此,以你的敏感性写作:'私人attr_writer:has_sauce'就像黑板上的指甲,但写'class << Object def private_accessor(* names) names.each do | name | attr_accessor name private“#{name} =” end end end private_accessor:has_sauce'感觉丝绸般光滑吗? – 7stud 2014-09-02 19:59:52

2

private可以采取象征阿根廷,所以......

class Pancake 
    attr_accessor :has_sauce 
    private :has_sauce= 
end 

class Pancake 
    attr_reader :has_sauce 
    attr_writer :has_sauce; private :has_sauce= 
end 

等等

但是,什么是与 “原始” 的实例变量的事?它们是你的实例的内部;唯一可以通过名称调用它们的代码是pancake.rb之内的代码,这都是你的。事实上,他们开始于@,我认为这让你说“blech”,是什么使他们私密。如果你愿意,可以把@想象成private的简写。

至于处理,我认为你的直觉是好的:如果可以的话在构造函数中进行处理,或者在必要时在自定义存取器中进行处理。

+0

我没有解释我不喜欢在这种情况下使用实例变量。但为了进一步解释它,我使用的一个原则是:如果你有一个访问器,使用访问器而不是它隐藏的原始变量或机制。原始变量是实现,访问者是接口。我是否应该忽略界面,因为我是内部的实例?如果它是一个attr_accessor,我不会。 – 2014-08-29 16:25:43

+0

但谢谢你的关注。 – 2014-08-29 16:27:16

+1

然后'attr_accessor:bar;私人:酒吧=;'是你想要的。 – AlexChaffee 2014-09-02 14:41:45

0

直接在您的类中引用实例变量没有任何问题。无论如何,无论您是将这些方法设置为公开还是私人,attr_accessor都只是间接地做到这一点。

在这个特殊的例子,它可以帮助认识到toppings很可能要保存用于其它目的的属性,并且has_sauce是一个“虚拟属性”,该模型的特征上基本配料属性这是相关的。

像这样的东西可能会觉得更清洁:

class Pancake 
    def initialize(toppings) 
    @toppings = toppings 
    end 

    def has_sauce? 
    sauces = [:maple, :butterscotch] 
    (@toppings & sauces).any? 
    end 
end 

取决于你是否不暴露attr_accessor :toppings为好。如果你只是扔掉配料,那么你的班级就少一个Pancake和更多的PancakeToppingDetector;)

+0

我看到一个'虚拟属性'是我们在使用框架,ORM等时被迫执行的东西。有些东西可以让我们将代码注入到任何元编程的路径中。在这样一个简单的PORO中,我看不出它有什么意义;这只是一种方法。 :) – 2016-04-08 15:27:34

+0

随着整个'在整个代码中使用实例变量'的事情,我会告诉你在这: https://github.com/dbrady/scoped_attr_accessor 有人为我完成了工作。 :) – 2016-04-08 15:28:21

+0

此外,桑迪梅斯在POODR中提到这一点。我记得,她还主张在方法中包装裸露的实例变量,即使它们只用于内部。它有助于避免以后的重构。 – 2016-04-08 15:30:01