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
\$\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
\$\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\$
    – Freeflow
    Commented Nov 2, 2019 at 17:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.