3

I am reading up on creating class factories here: https://rubberduckvba.wordpress.com/2018/04/24/factories-parameterized-object-initialization/ and I am confused why they are making the implemented functions private, wouldn't we want them to be public so we can access them?

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "Something"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Type TSomething
    Bar As Long
    Ducky As String
End Type

Private this As TSomething
Implements ISomething

Public Function Create(ByVal initialBar As Long, ByVal initialDucky As String) As ISomething
    With New Something
        .Bar = initialBar
        .Ducky = initialDucky
        Set Create = .Self
    End With
End Function

Public Property Get Self() As ISomething
    Set Self = Me
End Property

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

Friend Property Let Bar(ByVal value As Long)
    this.Bar = value
End Property

Public Property Get Ducky() As String
    Ducky = this.Ducky
End Property

Friend Property Let Ducky(ByVal value As String)
    this.Ducky = value
End Property

Private Property Get ISomething_Bar() As Long
    ISomething_Bar = Bar
End Property

Private Property Get ISomething_Ducky() As String
    ISomething_Ducky = Ducky
End Property

Also, why do you need to provide get and let properties for public variables in an interface?

Name
  • 209
  • 1
  • 4
  • Looking at your code, i don't see a `private function` (although you could have `private function`). There is a difference between a `function` and a `property`. I suspect you will come across it at some point. Also, as your query is not because you have an issue with your code, you might want to post this in [Code Review](https://codereview.stackexchange.com/) site – Zac Jun 17 '20 at 13:42
  • 3
    @Zac FYI CR is for peer-reviewing code you wrote yourself, not getting to understand code someone else wrote. – Mathieu Guindon Jun 17 '20 at 13:44
  • @MathieuGuindon: that's true, my bad. Still not sure if this is the place for this query though – Zac Jun 17 '20 at 13:46

1 Answers1

7

They should be Private.

The reason is because how interfaces work in VBA: the Public members of a class module define its default interface. That means the public members of Class1 define what members Class2 must implement if it Implements Class1.

So if you make Class1_DoSomething public, then you're exposing that member on the default interface of Class2, and that's... not pretty at all.

What interface you access an object with, is determined by how you declare it.

Dim thing As Class1
Set thing = New Class1

If thing is or implements Class1, then the code after this declaration can invoke all the members exposed by the default interface of Class1 (i.e. its public members).

If Class1 implements ISomething and we declare it like this:

Dim thing As ISomething
Set thing = New Class1

Now the members we get to work with are the members defined by the public members of the ISomething class/interface.

When you implement an interface or handle events, you should never manually type the signatures; instead, pick the interface (or event provider) from the upper-left dropdown in the code pane, then pick a member from the upper-right dropdown: the VBE automatically creates the correct procedure with the correct signature, and it's always going to be a Private member - rule of thumb, anything that has an underscore in its name in VBA has no business being Public


As for why you must supply Get and Let accessors for what you defined as a public field (/variable) on an interface class... Fields are implementation details, they should never be Public in the first place. Objects expose properties, not fields - keep fields for the private internal state of the implementing class.

The reason is technical: VBA code gets compiled into a COM type library, and that library sees your public variable and says "that's going to have to be a PUT and a GET method", and the VBA code implementing that interface thus needs to implement a property for every public field, because public fields compile down to properties.

This does have interesting implications with regards to the practice of exposing a public field on a class module (breaks encapsulation vs compiles down to a property anyway!), but that is a whole other discussion.

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
  • `They should be Private` - but, if you make them Public, it will work anyway, even though the method names will be different between the class and the interface. – GSerg Jun 17 '20 at 14:01
  • @GSerg yes, it will work, but then your default interface is a mutant =) – Mathieu Guindon Jun 17 '20 at 14:06
  • Do I understand this correctly? If I have Class1 which acts as the interface, let's say it has a declaration called DoSomething which is a public function, I declare a variable called instanceA As Class1, Then I set the variable instanceA = New Class2, and class 2 implements class one, so that means in the class module for Class2, there is somewhere in there a function called Class1_DoSomething, now if that is private, and I use instanceA, I can only do instanceA.DoSomething, but if that is public, then are you saying I can also do instanceA.Class1_DoSomething? – Name Jun 17 '20 at 15:28
  • Do functions and properties within a class module default to public if you don't specify public or private? – Name Jun 17 '20 at 15:36
  • Now what about other functions, not in the interface, but public within the class that is implementing an interface, say we have Public Function DoSomethingElse which isn't in the interface Class1, but is public in class2, I had tried this and I can't access it? from a variable declared as Class1 – Name Jun 17 '20 at 15:53
  • You access an object's members via its declared interface, so you don't do that. Instead you define what the formal public interface needs to be for an object, and implement it. i.e. if I make a `Person` class, `Person` defines the members needed to manipulate the state of a `Person`, and the class implements an `IPerson` interface that defines the members the rest of the code work with: nothing should ever need to alter `IPerson.Name`, and if there's code responsible for doing that, it can work with the `Person` default interface directly. The guiding principle is "code against interfaces". – Mathieu Guindon Jun 17 '20 at 16:01
  • My confusion lies in the fact that I had created a contrived case where I actually made Class1_DoSomething public just to see what happens, I like to see for myself, but even when I made it public, I couldn't access Class1_DoSomething from my variable? Does that make sense? – Name Jun 17 '20 at 16:36
  • You would have to declare your variable `As Class2` to access the object via its `Class2` /default interface, because the `Class1_DoSomething` member doesn't exist on the `Class1` interface (`DoSomething` does). My recommendation is to keep the default interface for internal use (e.g. property injection via factory method), keep a class' default instance *stateless*, and define an explicit, formal interface for working with that object. – Mathieu Guindon Jun 17 '20 at 16:44
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/216150/discussion-between-anthony-schrayer-and-mathieu-guindon). – Name Jun 17 '20 at 16:50