AS3中的get/set方法和封装

4

我经常看到以下方法被描述为实现get/set方法的“正确”方式:

public class Foo {
    private var _someVar:SomeClass;

    public function get someVar():SomeClass {
       return _someVar;
    }

    public function set someVar(newValue:SomeClass):void {
      _someVar = newValue;
    }
}

现在,因为AS3始终返回对Object类的引用,当我们使用“get”方法时,我们获得了对我们的private变量的引用 => 封装被破坏了。
即使我们没有设置方法,我们也可以修改privar var!那么将其设置为私有有什么目的呢?
我找到的唯一解决方案是在我们的get方法中返回“_someVar”的克隆版本,但我从未在任何示例中看到过这种情况。所以我觉得我在这里失去了一些东西。
你是从你的getter返回克隆对象还是接受封装的破坏?
编辑 我理解set和get方法的工作原理,并且我理解它们的好处。我询问的是当我们通过引用返回私有变量时(如果我们的变量是Number、String、int等类型,则AS3始终按值而非引用返回,因此我们在这里没有问题),我们的“私有”访问是否被破坏了(我们无法在没有setter方法的情况下设置属性,但我们可以修改它!)。
请参阅此示例:
public class Foo {
    private var _someVar:Array; // note that this is a Object (not Number, String, etc)

    public function Foo(){
        _someVar = ['don't touch this!'];
    }

    public function get someVar():SomeClass {
       return _someVar;
    }

    // note that we don't have a setter

}


var f:Foo = new Foo(); 
var a:Array = f.someVar;
trace(a[0]); //  'don't touch this!'
a[0] = 'why not?'; 
trace(f.someVar[0]); // 'why not' 

因此,我们正在从外部更改私有变量,而且没有控制,即使我们没有setter方法。

5个回答

4
您在使用get/set函数时控制对成员变量的访问。例如,如果您希望该变量从外部是“只读”的,但在类实例内可以进行编辑,则创建一个get函数以便从外部读取,但不要创建set函数。这与使用私有常量不同,因为后者必须立即声明,并且无法从任何地方更改。
同样地,使用这些函数可以允许您为设置属性创建副作用。例如:
public function set foo(value:*):void{
    _foo = value;
    this.dispatchEvent(new Event("fooSet")); 
    // setting foo alerts interested parties
    // that the value of foo has changed
    // without them having to poll foo.
}

编辑:因为你更新了问题,使其更具体,这里是我自己的更新。

通常情况下不会这样做。如果你想要保护变量本身,则不会直接提供访问它的方式。这样做会违反“Demeter法则”。对于你特定的数组示例,你可以像这样做:

private var _someArray = [true,false];

function get someArray():Array{
    return _someArray.slice(); // Returns a clone of the original array.
}

作为另一个例子,使用一个理论上的复杂对象...
private var _someObject:SomeObject;

function get someObject():SomeObject{
    return _someObject; // "Wrong."  It breaks the law of demeter.
}

////// instead, you would do this.....

function get getSomeObjectsInt():int{
    return _someObject.foo; // where .foo is an int
} 


////// or this....

function doStuffWithFooObject():Boolean{
   return _someObject.doSomething(); // where doSomething returns a boolean;
}


///// or this.....

function performActionOnData(pData:String):String{
    return _someObject.someActionWithString(pData); 
}

最后一个例子很有趣,因为你不需要向世界展示你正在使用SomeObject来完成工作...你只是在宣传你自己可以做到。


我觉得我需要修改我的问题,显然我没有解释清楚 :) 对此很抱歉,英语不是我的母语 :( - Enrique
是的,我认为它确认了我的问题:如果我们想要仅给予属性及其内容读取访问权限,仅将该属性设置为私有并不定义setter是不够的,我们还需要在getter中返回我们私有属性的“副本”(值、克隆)。 - Enrique

1

你可以在所属类中修改私有变量,但是你不能在该类之外修改它。

具有getter和setter方法可以使开发人员对类拥有更多的控制权。

您的应用程序会不断增长,最终您可能希望您的类能够在获取或设置值之前对其进行某些操作。 您还可能希望您的类能够在每次设置值时调用某个方法。 当您拥有getter / setter方法时,您可以轻松实现此类功能。

此外,正如TheDarkIn1978所说,留出其中的一个方法可以使变量成为只写或只读,这将极大地有助于封装。


1
封装并不是防止类对私有属性进行引用。它是关于控制其他类可以访问哪些属性的。我认为这就是 OP 没有真正理解的地方。 - Clement Herreman
这不仅仅是控制其他类可以访问哪些属性的问题,还涉及到控制属性被访问的方式。 - Emanuil Rusev
这并不是真的。如果我没有setter,只有getter,我仍然可以修改(而不是设置)私有变量,因为我的getter返回对该私有成员的引用而不是副本!因此,我正在从类外部更改私有变量,而没有控制。也许这个词不是“封装”,但我们正在绕过访问限制。 - Enrique
除此之外,你可能已经意识到你通常不需要一个 setter;只有在需要外部能够更改私有变量的引用时,才需要一个 setter。 - user610650
@Enrique,你可以修改对象,但是你不能修改变量。无论你在类外对对象做什么,变量仍将持有对该对象的引用。 - Emanuil Rusev
显示剩余4条评论

0

如果你要进行封装,那么就不应该直接访问复杂对象(通过引用传递)。你应该考虑一下你希望其他类能够使用你的“SomeClass”做什么。如果你真的需要传递整个类并且不想它成为一个引用,那么就添加一个克隆方法。如果你只是希望其他类更新属于“SomeClass”的某些数据,则为该数据提供setter,并将其直接应用于“SomeClass”实例。这确实需要更多的代码,但可以实现封装类的目标。


0

如果我需要知道变量是否已被修改以便改变其他事情,我经常使用getter和setter。

例如,如果我有:

public var prop : Number;
private var prop2 : Number;

我希望prop2随时等于prop + 10,但我不知道何时更新prop2,但我可以这样做:

private var _prop : Number;
private var _prop2 : Number;

public function set prop(newValue : Number):void {
  _prop = newValue;
  _prop2 = prop + 10;
}

嗨@gabitzish,这不是我的问题,我知道如何设置和获取方法的工作原理。此外,您在示例中使用Number,AS3中的Number按值返回而不是引用,因此如果我们的变量是Number(或int、String、Bool等),我们不会破坏私有访问。 - Enrique

0

上面的代码与简单地编写以下内容完全相同:

public var someVar:SomeClass;

然而,如果您想将此变量设置为只读只写,则分别为私有变量提供公共getter或公共setter。

此外,setter和getter函数允许您管理传递的参数、触发事件等。例如,假设您有一个变量mySmallNumber:Number,它应该只接受小于10的值:

private var mySmallNumberProperty:Number;

public function set mySmallNumber(value:Number):void
     {
     if     (value < 10)
            mySmallNumberProperty = value;
            else
            throw new ArgumentError("mySmallNumber must be less than 10");
     }

public function get mySmallNumber():Number
     {
     return mySmallNumberProperty;
     }
  • 请不要因为我没有遵循下划线前缀的使用而抨击我。这只是一个风格问题。虽然它是“标准”的,但我认为它非常丑陋。

1
你的私有变量是Number类型,这里没有问题,因为AS3返回的是值而不是引用。例如将mySmallNumberProperty设置为"Array"类型,那么你就不能再将其设置为只读,因为你从getter中返回了对该私有变量的引用,因此可以从外部修改它。 - Enrique

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接