4
\$\begingroup\$

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 = "[email protected]"
 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
asked Nov 2, 2019 at 13:44
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

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
answered Nov 2, 2019 at 14:18
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Except Self is a good name for a Property Get procedure that returns Me and does nothing else - I like the idea/approach very much, but it needs a name that better conveys the nature of the side-effects here, like Init or Initialize, maybe. \$\endgroup\$ Commented Nov 2, 2019 at 16:28
  • 1
    \$\begingroup\$ I had a think for a name and with the idea of something finished and ready to be presented I came up with Debutante? \$\endgroup\$ Commented Nov 2, 2019 at 17:12

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.