PDA

View Full Version : how do I work with objects (pass objects to objects?)



next
01-10-2014, 03:54 PM
I'm trying to write a service calculator in VBA using objects, but I'm pretty noob when it comes to OOP :(
I have several classes:
cService - service / job

Option Explicit

Private description As String
Private crew As cCrew

' get / set service description
Public Property Let setDescription(value As String)
description = value
End Property

Public Property Get getDescription() As String
getDescription = description
End Property

'set service crew
Public Property Let setCrew(value As cCrew)
crew = value
End Property

cCrew - a company that performed the service. Responsible for holding data such as service time, number of workers and calculations of time

Option Explicit

Private code As String
Private techCount As Single
Private serviceStart As Date
Private serviceEnd As Date

' get / set crew code
Public Property Let setCode(value As String)
code = value
End Property
Public Property Get getCode() As String
getCode = code
End Property

' get / set number of techs
Public Property Let setTechCount(value As Single)
techCount = value
End Property
Public Property Get getTechCount() As Single
getTechCount = techCount
End Property

' get set service start
Public Property Let setServiceStart(value As Date)
serviceStart = value
End Property
Public Property Get getServiceStart() As Date
getServiceStart = serviceStart
End Property

' get set service end
Public Property Let setServiceEnd(value As Date)
serviceEnd = value
End Property
Public Property Get getServiceEnd() As Date
getServiceEnd = serviceEnd
End Property

Public Property Get getHours() As Integer
Dim rawTime As Single, result As Single, roundedTime As Double
Dim base As Single, remainder As Single

'calculate service time
rawTime = DateDiff("n", serviceStart, serviceEnd)
result = Round(rawTime / 60, 2)
base = WorksheetFunction.Floor(result, 1)
remainder = result - base

'round to the nearest quarter
Select Case (remainder)
Case 0 To 0.12
roundedTime = base
Case 0.12 To 0.37
roundedTime = base + 0.25
Case 0.38 To 0.62
roundedTime = base + 0.5
Case 0.63 To 0.86
roundedTime = base + 0.75
Case Else
roundedTime = base + 1
End Select

getHours = roundedTime
End Property

Public Property Get getTotalServiceTime() As Integer
getTotalServiceTime = getHours * techCount
End Property


Then I have my sandbox. What I'm trying to do here is create a new service and crew objects, then assign the crew to the service, but it doesn't seem to work. I get invalid use of property error:

Option Explicit
Sub test()
Dim crew As cCrew
Dim service As cService

Set crew = New cCrew
Set service = New cService

crew.setCode = "XY-000"

service.setCrew crew
Debug.Print service.crew.getCode
End Sub
How do I go about coding this?

P.S. I tried:
service.setCrew crew
service.setCrew(crew)
service.setCrew = crew
All give me an error

SamT
01-10-2014, 06:20 PM
You have to "Set" Objects

Public Property Let setCrew(value As cCrew)
crew = value
End Property should read
Public Property Set setCrew(value As cCrew)
crew = value
End Property

Matching Get, Let, and Set properties MUST have the same name:
Public Property Let setCode(value As String)
code = value
End Property
Public Property Get getCode() As String
getCode = code
End Property Should read
Public Property Let Code(value As String)
code = value
End Property
Public Property Get Code() As String
Code = code 'Note other kind of error here
End Property

It is important that you use a convention that is recognizable industry wide.

Take a look at the pattern of properties below. You will notice that they all follow the same pattern of
Let/Set Name(newName)
mName = newName
End
get Name
Name = mName
End
A glance at any Property Procedure tells you if there is a discrepancy. Note also that all other code is outside of any Property Procedure.

When you start creating larger Classes you will find a RegEx Search and Replace editor invaluable. I use UltraEdit with Macros and I can convert a list of module level variable into twin Property subs in less than 2 seconds per hundred. Even with out the macros I can do hundreds as fast as I can do a few.

This is how I would write it. Note that all properties only have three possible prefixes "m" for all module level variables except; "o" for Object Variables, and "new" for Property Let/Set Parameters. Module level Object variables for Objects that will be passed into the class are declared as "Object," not as a cClass.

.
Option Explicit
'Sandbox

Sub test()
Dim MyCrew As cCrew
Dim Service As cService

Set MyCrew = New cCrew
Set Service = New cService

MyCrew.WorkCode = "XY-000"

Set Service.Crew = MyCrew
Debug.Print Service.Crew.WorkCode
End Sub

Option Explicit
'Sevice

Private mDescription As String
Private oCrew As Object

Public Property Let Description(newDescription As String)
mDescription = newDescription
End Property

Public Property Get Description() As String
Description = mDescription
End Property

'Note how a Property that has "As Object" in the Declaration line
'must have a variable with "o" as the prefix
Public Property Set Crew(newCrew As Object)
Set oCrew = newCrew
End Property

Public Property Get Crew() As Object
Set Crew = oCrew
End Property

Option Explicit
'Crew

Private mWorkCode As String
Private mTechCount As Single
Private mServiceStart As Date
Private mServiceEnd As Date

Public Property Let WorkCode(newWorkCode As String)
mWorkCode = newWorkCode
End Property
Public Property Get WorkCode() As String
WorkCode = mWorkCode
End Property

Public Property Let TechCount(newTechCount As Single)
mTechCount = newTechCount
End Property
Public Property Get getTechCount() As Single
TechCount = mTechCount
End Property

Public Property Let ServiceStart(newServiceStart As Date)
mServiceStart = newServiceStart
End Property
Public Property Get ServiceStart() As Date
ServiceStart = mServiceStart
End Property

Public Property Let ServiceEnd(newServiceEnd As Date)
mServiceEnd = newServiceEnd
End Property
Public Property Get ServiceEnd() As Date
ServiceEnd = mServiceEnd
End Property

Public Property Get Hours() As Single
Hours = CalculateHours
End Property

Public Property Get TotalServiceTime() As Single
TotalServiceTime = CalculateHours * mTechCount
End Property

Private Function CalculateHours() As Single
Dim rawTime As Single, result As Single, roundedTime As Single
Dim base As Single, remainder As Single

'calculate service time
rawTime = DateDiff("n", mServiceStart, mServiceEnd)
result = Round(rawTime / 60, 2)
base = WorksheetFunction.Floor(result, 1)
remainder = result - base

'round to the nearest quarter
Select Case (remainder)
Case 0 To 0.12
roundedTime = base
Case 0.12 To 0.37
roundedTime = base + 0.25
Case 0.38 To 0.62
roundedTime = base + 0.5
Case 0.63 To 0.86
roundedTime = base + 0.75
Case Else
roundedTime = base + 1
End Select

CalculateHours = roundedTime
End Function

I also noticed that you were calculating time as decimal numbers, but were returning it as an Integer. Gotta watch those number Types.

mikerickson
01-11-2014, 08:59 AM
You also have to use Set in the assignment statements

Public Property Set setCrew(value As cCrew)
SET crew = value
End Property

snb
01-11-2014, 09:59 AM
To illustrate the cCrew class see the attachment.

It can be practical to use discriminating name conventions for properties, variables and arguments.

next
01-13-2014, 07:42 AM
Thanks for the help guys, I'll be gluing this together later today :)
VBA seems very different and limited compared to other OOP languages. Good thing M$ made coding excel in C# possible, hopefully I'll get to master it somewhere down the road.

Jan Karel Pieterse
01-13-2014, 08:47 AM
VBA is almost 20 years old, so I'm not surprised it lacks OOP :-)