Open retailcoder opened 4 years ago
Side note, should there be a ObjectOrientedOpportunities
inspection type?
This annotation would also enable automatically generating factory methods:
@Inject
annotation (factory method already present)@Inject
properties in the moduleValue
to the property name)The generated factory method could look like this:
'@Inject
Public Function Create({parameters}) As {ClassName}
If Me Is {ClassName} Then Err.Raise 5, TypeName(Me), "Factory method should only be invoked from default instance."
Dim result As {ClassName}
Set result = New {ClassName}
result.{Property} = {corresponding parameter value}
Set Create = result
End Function
The Extract Interface refactoring could then be enhanced to leverage this: when extracting an interface from a class module that has an @Inject
annotation on a Function
(i.e. has a factory method), Extract Interface could generate the Get
accessors for the @Inject
properties (optionally also generating the Let
accessors, but not by default), and make the factory method return the new interface instead of {ClassName}
. Or is that too much for Extract Interface to be doing and should be a separate thing?
Let me propose an alternative approach. A common OOP approach to object creation involves a combination of a 1) factory responsible for creation of an uninitialized object invoked as a "class method" (as in Python, with the closest VBA analog being default predeclared object instance) and 2) constructor, invoked as an "instance method" (as in Python, with the closest VBA analog being non-default object instance), which has the same signature as the corresponding factory, is invoked by the factory, and has access to private properties for proper initialization. Similarly to defining a "Create" factory, an "init" constructor method with the same signature can be defined and used in place of those setters, whose sole purpose is initialization of private instance properties.
I would want a decorator called @Constructor
because VBA doesn't allow for Constructor Overloading with different number of parameters. What are the uses for property injection outside of a constructor?
@A9G-Data-Droid
What are the uses for property injection outside of a constructor?
Setting aside the VBA language for a moment...Property Injection is an alternative to Constructor Injection. An object could use both for a single dependency - though I suspect that the need to use both forms of injection would be rare. Generally speaking, if a dependency can be injected via a constructor - it should be...and there is no need for Property Injection.
Property Injection can be used when there is already an acceptable Local Default implementation to fulfill the dependency. Consequently, the Local Default is not Constructor Injected. Dependencies injected using Property Injection are sometimes considered optional dependencies since they are not required to construct a valid instance. Property Injection is one mechanism of implementing the Open/Closed Principle.
Example:
Assume there is an IAudibleFeedback
interface with a single method called 'MakeNoise'. Further assume that a 'Feedback' class implements this interface and offers a Private Sub IAudibleFeedback_MakeNoise
implementation that uses the built-in Beep Statement
. In order to support optional IAudibleFeedback
implementations, the 'Feedback' object can use Property Injection. Internally, the 'Feedback' object might look something like:
Implements IAudibleFeedback
Private feedback As IAudibleFeedback
Public Property Set AudibleFeedback(ByVal RHS As IAudibleFeedback)
Set feedback = RHS
End Property
Private Sub IAudibleFeedback_MakeNoise()
If feedback Is Nothing then
Beep
Exit Sub
End If
feedback.MakeNoise
End Sub
Now assume someone has written a 'KlaxonHorn' object that also implements IAudibleFeedback
where calling IAudibleFeedback_MakeNoise
generates a klaxon horn sound. The 'KlaxonHorn' object's IAudibleFeedback
interface can be property injected using Property Set AudibleFeedback
to change the sound generated by the 'Feedback' object. The key point here is that, with Property Injection, the 'Feedback' object's default behavior can be changed without altering its source code.
Justification Write-only properties are a code smell, but so is having a useless get accessor just to shut off a warning about it (and then have an
@Ignore
annotation to shut off a warning about that accessor being useless). When a setter is only intended to be used for property injection from a factory method that's in the same class module,Description Let's introduce an
@Inject
annotation that tells theWriteOnlyProperty
inspection to ignore this member. The annotation should only be valid onPropertyLet
andPropertySet
class members... and at least oneFunction
member in the class.Additional context This annotation would make several new inspection opportunities to warn about misuse of these members. I can think of one that suggests using the
Friend
access modifier (for properties anyway), and another that warns of any usage outside the class module it's defined in (or outside@Inject
functions, if there are any). Function members with an@Inject
annotation can be validated to ensure they return one of the interfaces the class module can be used with (i.e. the class' default interface, or anyImplements
) - and then there can even be an inspection to suggest using an abstract interface (marked with@Interface
) when a factory method (the function marked with@Inject
) is returning the class' default/"concrete" interface.