我是否理解未正确使用getter和setter

时间:2018-04-13 08:05:22

标签: vba get set

在阅读了Yegor关于不使用getter和setter的this篇文章之后,这听起来像是对我有用的东西。

请注意,这个问题不是关于做得好还是坏,只有我正确实施

我想知道VBA中的以下两个例子,如果我理解正确的概念,以及我是否正确应用它。

标准方式是:

Private userName As String

Public Property Get Name() As String
    Name = userName
End Property
Public Property Let Name(rData As String)
    userName = rData
End Property

在我看来他的方式是这样的:

Private userName As String

Public Function returnName() As String
    returnName = userName
End Function

Public Function giveNewName(newName As String) As String
    userName = newName
End Function

根据我从上面两个例子中理解的是,如果我想更改userName的格式(让我们说以全部大写字母返回),那么我可以使用第二种方法执行此操作,而无需更改方法的名称提供名称 - 我可以让returnName指向userNameCaps属性。我程序中的其余代码仍然可以保持不变并指向userName方法。

但是如果我想用第一个例子做这个,我可以创建一个新属性,但是然后必须在程序中的任何地方更改我的代码以指向新属性......这是正确的吗?

换句话说,在第一个示例中,API从属性获取信息,在第二个示例中,API从方法获取信息。

3 个答案:

答案 0 :(得分:4)

你的第二个片段既不是惯用的也不是等同的。您链接到的那篇文章是关于Java的,这种语言没有任何对象属性的概念 - getFoo / setFoo只是Java中的约定

在VBA中:

Private userName As String

Public Property Get Name() As String
    Name = userName
End Property
Public Property Let Name(rData As String)
    userName = rData
End Property

最终等同于:

Public UserName As String

不相信?将这样的公共字段添加到类模块,比如Class1。然后添加一个新的类模块并添加:

Implements Class1

编译器将强制您实现Property GetProperty Let成员,以便可以履行Class1接口合同。

那么为什么要打扰属性呢?属性是一种工具,可以帮助封装

Option Explicit
Private Type TSomething
    Foo As Long
End Type
Private this As TSomething

Public Property Get Foo() As Long
    Foo = this.Foo
End Property

Public Property Let Foo(ByVal value As Long)
    If value <= 0 Then Err.Raise 5
    this.Foo = value
End Property

现在,如果您尝试为Foo分配负值,则会出现运行时错误:该属性是封装内部状态,只有该类知道并且能够mutate:调用代码没有看到或知道封装的值 - 它只知道Foo是一个读/写属性。 “setter”中的验证逻辑确保对象始终处于一致状态。

如果您想将属性分解为方法,那么您需要{get}的Function,并且分配将是Sub而不是Function。实际上,Rubberduck会告诉您,从未分配giveNewName的返回值存在问题:代码异味比“OMG您正在使用属性”更糟糕!”

函数返回值。子/方法做某事 - 在对象/类的情况下,某些东西可能意味着改变内部状态。

但是避免Property Let只是因为一些Java家伙说吸气剂&amp; setter是邪恶的,你只是让你的VBA API比它需要的更混乱 - 因为VBA理解属性,而Java则不然。但是,C#和VB.NET会这样做,所以如果有的话,这些语言的原理将比Java更适用于VBA,至少在属性方面如此。请参阅Property vs Method

VB中的FWIW公共成员名称按惯例为PascalCasecamelCase公共成员名称是Java的东西。请注意标准库中的所有内容都以Capital首字母开头?

答案 1 :(得分:1)

在我看来,你刚给了属性访问者新的名字。它们在功能上是相同的。

我认为不使用getter / setter的想法意味着你不要尝试从外部修改对象的状态 - 因为如果这样做,对象只不过是用户定义的类型,一个简单的数据集合。对象/类应该由它们的行为来定义。它们包含的数据应仅用于启用/支持该行为 这意味着您不会告诉对象它必须是什么或者您想要它保存什么数据。你告诉它你想要它做什么或发生了什么。然后,对象本身决定如何修改其状态。

对我而言,您的示例类似乎有点过于简单而无法作为示例。目前尚不清楚预期目的是什么:目前你最好只使用变量UserName

看看this answer to a related question - 我认为它提供了一个很好的例子。

关于你的编辑:

  

根据我从上面两个例子中理解的是,如果我想要的话   更改userName的格式(假设以全部大写形式返回),   然后我可以用第二种方法做到这一点而不改变名称   给出名称的方法 - 我可以让returnName   指向userNameCaps属性。我程序中的其余代码   仍然可以保持不变并指向方法iserName。

     

但如果我想用第一个例子来做这个,我可以做一个新的   属性,但随后必须在程序中的任何地方更改我的代码   很好地指出新的财产......这是正确的吗?

实际上,你在这里描述的内容在两种方法中都是可能的。你可以拥有一个房产

Public Property Get Name() As String
    ' possibly more code here...
    Name = UCase(UserName)
End Property

或等效函数

Public Function Name() As String
    ' possibly more code here...
    Name = UCase(UserName)
End Function

只要您只更改属性/函数体,就不需要调整外部代码。保持属性的/函数的签名(第一行,包括Public语句,其名称,类型及其参数的顺序和类型)保持不变,您不需要更改类以外的任何内容以适应。 / p>

答案 2 :(得分:1)

Java文章正在制定一些不仅限于Java的哲学设计立场:一般建议是严格限制有关如何实现类的任何细节,以避免使代码难以维护。将这些建议纳入VBA术语并不是无关紧要的。

微软普及了Property的想法,这实际上是一种伪装成一个领域(即任何花园变量)的方法(或两个)。将吸气剂和固定器组合在一起是一种干净利落的方式。除此之外,实际上,在幕后,它仍然只是一组函数或子程序,可以作为您班级的访问者。

了解VBA不会执行类,但它会执行接口。这就是&#34;类模块&#34;是:(匿名)类的接口。当您说Dim o As New MyClassModule时,VBA会调用一些工厂函数,该函数返回与MyClassModule一起的类的实例。从那时起,o引用了接口(接口又连接到实例中)。正如@Mathieu Guindon所证明的,无论如何,类模块中的Public UserName As String真正成为幕后的Property。为什么?因为类模块是一个接口,而接口是一组(指向)函数和子程序。

至于哲学设计的立场,这里真正重要的不是要做出太多的承诺。如果UserNameString,则必须始终为String。此外,它必须始终可用 - 您无法从课程的未来版本中删除它! UserName可能不是最好的例子(毕竟,为什么String不能满足所有需求?出于什么原因UserName可能变得多余?)。但确实发生在课堂制作时似乎是一个好主意的事情变成了一个大蠢事。想象一下Public TwiddlePuff As Integer(或者getTwiddlePuff() As IntegersetTwiddlePuff(value As Integer))只是为了找出{很久以后!'Integer已经不够了,也许它应该有已Long。或者是Double。如果您现在尝试更改TwiddlePuff,那么在Integer时编译的任何内容都可能会中断。因此,制作新代码的人可能会很好,也许大多数人仍然需要使用一些现在遇到问题的旧代码。

如果TwiddlePuff被证明是一个非常大的设计错误,它应该首先不应该存在?好吧,删除它会带来一系列令人头疼的问题。如果其他地方一直使用TwiddlePuff,那意味着一些人可能会在他们手上进行大量的重构工作。这可能不是最糟糕的 - 如果你的代码特别编译为原生二进制文件,这会造成一个非常大的混乱,因为一个接口是关于一组函数指针,它们以非常特定的方式排列和排序。

太重申,不要做出太多的承诺。仔细考虑一下你将与他人分享的内容。 Properties-getters-setters-accessors是可以的,但必须谨慎而谨慎地使用。如果您正在制作的代码是您要与他人共享的代码,而其他人将把它作为更大的代码系统的一部分使用,那么上述所有内容都很重要,而这些代码可能会分享他们的更大的代码系统,甚至更多人将在更大的代码系统中使用它。

这就是为什么可能最大程度地隐藏实现细节被视为面向对象编程的基础。