Skip to main content
Code Review

Return to Answer

adds caveat regarding solution constraints
Source Link
ThunderFrame
  • 2k
  • 2
  • 16
  • 28

EDIT Mat's Mug has the best approach in going for an Interface and an add-in, but if you constrain the requirements to a self-contained solution, and you assume the user of the class is unfamiliar with interfaces, then you can have everything in a single class.

Your predeclared instance has the private fields and property gets to facilitate passing the variables to new instances. I've added a State property (replacing your IsMaking property) with a Friend scope (should the class end up in an add-in), and I've used VBA's ability to hide an emum member by prefixing it with an underscore, to obfuscate the values that State could otherwise be (i.e. Consumers of the class don't need to know what State does, and only know that State can be Ready.

Your predeclared instance has the private fields and property gets to facilitate passing the variables to new instances. I've added a State property (replacing your IsMaking property) with a Friend scope (should the class end up in an add-in), and I've used VBA's ability to hide an emum member by prefixing it with an underscore, to obfuscate the values that State could otherwise be (i.e. Consumers of the class don't need to know what State does, and only know that State can be Ready.

EDIT Mat's Mug has the best approach in going for an Interface and an add-in, but if you constrain the requirements to a self-contained solution, and you assume the user of the class is unfamiliar with interfaces, then you can have everything in a single class.

Your predeclared instance has the private fields and property gets to facilitate passing the variables to new instances. I've added a State property (replacing your IsMaking property) with a Friend scope (should the class end up in an add-in), and I've used VBA's ability to hide an emum member by prefixing it with an underscore, to obfuscate the values that State could otherwise be (i.e. Consumers of the class don't need to know what State does, and only know that State can be Ready.

Source Link
ThunderFrame
  • 2k
  • 2
  • 16
  • 28

Your predeclared instance has the private fields and property gets to facilitate passing the variables to new instances. I've added a State property (replacing your IsMaking property) with a Friend scope (should the class end up in an add-in), and I've used VBA's ability to hide an emum member by prefixing it with an underscore, to obfuscate the values that State could otherwise be (i.e. Consumers of the class don't need to know what State does, and only know that State can be Ready.

Setting up some Enums

As @Mat'sMug pointed out, the error values could use their own Enum:

Public Enum pointError
 errNotCreatable = vbObjectError + 100
 errOnlyCallableFromDefault = vbObjectError + 101
End Enum

And, I'll use an Enum for setting the state of the predeclareed instance. It's either ready, or it's Initializing a new Point. Note the use of a leading _ to make the non-default enum member hidden, and the use of square-brackets to make the syntax valid.

Public Enum pointState
 Ready = 0
 [_InitializingNewPoint] = 1
End Enum

Making fields more friendly

Then, again as per Mat'sMug's suggestion, I'm using a private type to store fields:

Private Type TPoint
 X As Double
 Y As Double
 State As pointState
End Type
Private this As TPoint

Hijacking the predeclared instance's fields

In the factory method Create, I temporarily set the state and X/Y fields of the default instance, and then in the Class_Initialize event of the new instance, I read those fields though the default instance's getters. As such, I've relaxed the conditions around being able to access the default instance's getters, but this could easily be re-added. The default Point will always have X and Y's getters return 0, in much the same way that the default Integer will always have default value 0.

 Public Function Create(X As Double, Y As Double) As Point
 If Me Is Point Then
 this.State = pointState.[_InitializingNewPoint]
 'Hijack the default instance's X and Y properties
 this.X = X
 this.Y = Y
 Set Create = New Point
 'Restore the default instance's X and Y properties
 this.X = 0
 this.Y = 0
 this.State = pointState.Ready
 Else
 Err.Raise pointError.errOnlyCallableFromDefault, "Point", "Invalid instance use of Create - Use Point.Create to instatiate new Point objects."
 End If
 End Function

Complete Code

This is a pre-declared class. I require that new instances of Point be created from the default instance, and only the default instance of Point (which means that the default instance could do useful things like tracking the number of Point instances), but it isn't much work to facilitate any instance of Point being a valid factory.

Option Explicit
Public Enum pointError
 errNotCreatable = vbObjectError + 100
 errOnlyCallableFromDefault = vbObjectError + 101
End Enum
Public Enum pointState
 Ready = 0
 [_InitializingNewPoint] = 1
End Enum
Private Type TPoint
 X As Double
 Y As Double
 State As pointState
End Type
Private this As TPoint
Private Sub Class_Initialize()
 If Not Me Is Point Then
 If Point.State = pointState.[_InitializingNewPoint] Then
 this.X = Point.X
 this.Y = Point.Y
 Else
 Err.Raise pointError.errNotCreatable, "Point", "Invalid use of New Keyword - Use Point.Create to instatiate new Point objects."
 End If
 End If
End Sub
Public Property Get X()
 X = this.X
End Property
Public Property Get Y()
 Y = this.Y
End Property
Friend Property Get State() As pointState
 State = this.State
End Property
Public Function Create(X As Double, Y As Double) As Point
 If Me Is Point Then
 this.State = pointState.[_InitializingNewPoint]
 'Hijack the default instance's X and Y properties
 this.X = X
 this.Y = Y
 Set Create = New Point
 'Restore the default instance's X and Y properties
 this.X = 0
 this.Y = 0
 this.State = pointState.Ready
 Else
 Err.Raise pointError.errOnlyCallableFromDefault, "Point", "Invalid instance use of Create - Use Point.Create to instatiate new Point objects."
 End If
End Function

Usage:

 Dim p As Point
 Set p = Point.Create(1, 2)
 Debug.Print p.X, p.Y
 
 Dim o As New Point
 Debug.Print ObjPtr(o) 'Throws: Invalid use of New Keyword - Use Point.Create to instatiate new Point objects.
 
 Dim q As Point
 Set q = New Point 'Throws: Invalid use of New Keyword - Use Point.Create to instatiate new Point objects.
 
 Dim r As Point
 Set r = p.Create(1, 2) 'Throws: Invalid instance use of Create - Use Point.Create to instatiate new Point objects.
lang-vb

AltStyle によって変換されたページ (->オリジナル) /