Problem
I’ve reproduced the example of this video where dependency inversion principle is explained by Tim Corey based on C#
I had some trouble understanding the scope of the Logger and MessageSender properties and how to set them with the Create function inside the Chore class
Also:
With Rubberduck inspections I receive Property *** has no getter
I would appreciate your feedback over the implementation
Link to the finished file (Dependency inversion principle applied)
Link to the initial file (Before DIP applied)
Note: Used Rubberduck VBA to set the attributes
To reproduce just run the TestProgram sub in the Macros module
Module:Macros
'@Folder("Program")
Option Explicit
Public Sub TestProgram()
Dim programtest As New Program
programtest.Main
End Sub
Class:Chore
'@Folder("Chores")
'@PredeclaredID
Option Explicit
Private Type TChore
ChoreName As String
Owner As person
HoursWorked As Double
IsComplete As Boolean
Logger As ILogger
MessageSender As IMessageSender
End Type
Private this As TChore
Implements IChore
Public Sub PerformedWork(ByVal hours As Double)
this.HoursWorked = this.HoursWorked + hours
this.Logger.Log "Performes work on " & this.ChoreName
End Sub
Public Sub CompleteChore()
IsComplete = True
this.Logger.Log "Completed " & this.ChoreName
this.MessageSender.SendMessage this.Owner, "The chore " & this.ChoreName & " is complete"
End Sub
Public Property Set Logger(ByVal Logger As ILogger)
Set this.Logger = Logger
End Property
Public Property Set MessageSender(ByVal MessageSender As IMessageSender)
Set this.MessageSender = MessageSender
End Property
Public Property Get Self() As Chore
Set Self = Me
End Property
Public Function Create(ByVal Logger As ILogger, ByVal MessageSender As IMessageSender) As Chore
With New Chore
Set .Logger = Logger
Set .MessageSender = MessageSender
Set Create = .Self
End With
End Function
Public Property Get Owner() As IPerson
Set Owner = this.Owner
End Property
Public Property Let Owner(ByVal value As IPerson)
Set this.Owner = value
End Property
Public Property Get ChoreName() As String
ChoreName = this.ChoreName
End Property
Public Property Let ChoreName(ByVal value As String)
this.ChoreName = value
End Property
Public Property Get HoursWorked() As Double
HoursWorked = this.HoursWorked
End Property
Public Property Let HoursWorked(ByVal value As Double)
this.HoursWorked = value
End Property
Public Property Get IsComplete() As Boolean
IsComplete = this.IsComplete
End Property
Public Property Let IsComplete(ByVal value As Boolean)
this.IsComplete = value
End Property
Private Property Set IChore_Owner(ByVal Owner As IPerson)
Set this.Owner = Owner
End Property
Private Property Get IChore_Owner() As IPerson
Set IChore_Owner = this.Owner
End Property
Private Sub IChore_PerformedWork(ByVal hours As Double)
PerformedWork hours
End Sub
Private Sub IChore_CompleteChore()
CompleteChore
End Sub
Private Property Get IChore_ChoreName() As String
IChore_ChoreName = this.ChoreName
End Property
Private Property Let IChore_ChoreName(ByVal value As String)
this.ChoreName = value
End Property
Private Property Get IChore_HoursWorked() As Double
IChore_HoursWorked = this.HoursWorked
End Property
Private Property Let IChore_HoursWorked(ByVal value As Double)
this.HoursWorked = value
End Property
Private Property Get IChore_IsComplete() As Boolean
IChore_IsComplete = this.IsComplete
End Property
Private Property Let IChore_IsComplete(ByVal value As Boolean)
this.IsComplete = value
End Property
Class:Emailer
'@Folder("MessageSenders")
'@PredeclaredID
Option Explicit
Implements IMessageSender
Public Sub SendMessage(ByVal person As IPerson, ByVal message As String)
Debug.Print "Simulating sending an email to " & person.EmailAddress & " saying " & message
End Sub
Private Sub IMessageSender_SendMessage(ByVal person As IPerson, ByVal message As String)
SendMessage person, message
End Sub
Class:Factory
'@Folder("Program")
'@PredeclaredID
Option Explicit
Public Function CreatePerson() As IPerson
Set CreatePerson = New person
End Function
Public Function CreateChore() As IChore
Set CreateChore = Chore.Create(CreateLogger, CreateMessageSender)
End Function
Public Function CreateLogger() As ILogger
Set CreateLogger = New Logger
End Function
Public Function CreateMessageSender() As IMessageSender
Set CreateMessageSender = New Emailer
End Function
Class:IChore
'@Folder("Chores")
Option Explicit
Public Sub PerformedWork(ByVal hours As Double)
End Sub
Public Sub CompleteChore()
End Sub
Public Property Get ChoreName() As String
End Property
Public Property Let ChoreName(ByVal value As String)
End Property
Public Property Get HoursWorked() As Double
End Property
Public Property Let HoursWorked(ByVal value As Double)
End Property
Public Property Get IsComplete() As Boolean
End Property
Public Property Let IsComplete(ByVal value As Boolean)
End Property
Public Property Get Owner() As IPerson
End Property
Public Property Set Owner(ByVal value As IPerson)
End Property
Class:ILogger
'@Folder("Loggers")
Option Explicit
Public Sub Log(ByVal message As String)
End Sub
Class:IMessageSender
'@Folder("MessageSenders")
Option Explicit
Public Sub SendMessage(ByVal person As IPerson, ByVal message As String)
End Sub
Class:IPerson
'@Folder("People")
Option Explicit
Public Property Get FirstName() As String
End Property
Public Property Let FirstName(ByVal value As String)
End Property
Public Property Get LastName() As String
End Property
Public Property Let LastName(ByVal value As String)
End Property
Public Property Get PhoneNumber() As String
End Property
Public Property Let PhoneNumber(ByVal value As String)
End Property
Public Property Get EmailAddress() As String
End Property
Public Property Let EmailAddress(ByVal value As String)
End Property
Class:Logger
'@Folder("Loggers")
'@PredeclaredID
Option Explicit
Implements ILogger
Public Sub Log(ByVal message As String)
Debug.Print "Write to console: " & message
End Sub
Private Sub ILogger_Log(ByVal message As String)
Log message
End Sub
Class:Person
'@Folder("People")
Option Explicit
Private Type TPerson
FirstName As String
LastName As String
PhoneNumber As String
EmailAddress As String
End Type
Private this As TPerson
Implements IPerson
Public Property Get FirstName() As String
FirstName = this.FirstName
End Property
Public Property Let FirstName(ByVal value As String)
this.FirstName = value
End Property
Public Property Get LastName() As String
LastName = this.LastName
End Property
Public Property Let LastName(ByVal value As String)
this.LastName = value
End Property
Public Property Get PhoneNumber() As String
PhoneNumber = this.PhoneNumber
End Property
Public Property Let PhoneNumber(ByVal value As String)
this.PhoneNumber = value
End Property
Public Property Get EmailAddress() As String
EmailAddress = this.EmailAddress
End Property
Public Property Let EmailAddress(ByVal value As String)
this.EmailAddress = value
End Property
Private Property Get IPerson_FirstName() As String
IPerson_FirstName = this.FirstName
End Property
Private Property Let IPerson_FirstName(ByVal value As String)
FirstName = value
End Property
Private Property Get IPerson_LastName() As String
IPerson_LastName = this.LastName
End Property
Private Property Let IPerson_LastName(ByVal value As String)
LastName = value
End Property
Private Property Get IPerson_PhoneNumber() As String
IPerson_PhoneNumber = this.PhoneNumber
End Property
Private Property Let IPerson_PhoneNumber(ByVal value As String)
PhoneNumber = value
End Property
Private Property Get IPerson_EmailAddress() As String
IPerson_EmailAddress = this.EmailAddress
End Property
Private Property Let IPerson_EmailAddress(ByVal value As String)
EmailAddress = value
End Property
Class:Program
'@Folder("Program")
Option Explicit
Public Sub Main()
Dim Owner As IPerson
Set Owner = Factory.CreatePerson
Owner.FirstName = "Tim"
Owner.LastName = "Corey"
Owner.EmailAddress = "tim@iamtimcorey.com"
Owner.PhoneNumber = "555-1212"
Dim newChore As IChore
Set newChore = Factory.CreateChore
newChore.ChoreName = "Take out the trash"
Set newChore.Owner = Owner
newChore.PerformedWork 3
newChore.PerformedWork 1.5
newChore.CompleteChore
End Sub
Class:Texter
'@Folder("MessageSenders")
'@PredeclaredID
Option Explicit
Implements IMessageSender
Public Sub SendMessage(ByVal person As IPerson, ByVal message As String)
Debug.Print "I am texting " & person.FirstName & " to say " & message
End Sub
Private Sub IMessageSender_SendMessage(ByVal person As IPerson, ByVal message As String)
SendMessage person, message
End Sub
Solution
An observation on your class constructors. At the moment you are using public properties to allow you to set the value of properties of a class after it has been created. You can take this to the next step which allows you to delete the setters for the public class members by passing the create parameters to the Self function.
In this way you can create objects with immutable properties from parameters that are provided at the time of object creation.
Public Function Create(ByVal Logger As ILogger, ByVal MessageSender As IMessageSender) As Chore
With New Chore
Set Create = .Self(Logger, MessageSender)
End With
End Function
Public Function Self(ByVal Logger As ILogger, ByVal MessageSender As IMessageSender) As Chore
' This code runs internal to the newly created instance
Set this.Logger = Logger
Set this.MessageSender = MessageSender
Set Self = Me
End Property