Refactoring a VbClassic user-defined Type into a Class is a language-specific case of ReplaceRecordWithDataClass.
Problem:
-
- This VisualBasic program has structures / records / VisualBasic user-defined "Types," and some behavior of the program should be more closely associated with this data. Encapsulation, polymorphism, and other OO concepts may also be appropriate.
Solution:
-
- Convert VisualBasic user-defined types to VisualBasic classes.
How To:
- Create a class of the same name as the Type.
- Copy Type members to the class and make them Private member variables.
- Provide Property Get/Set/Let methods to enable access to the member variables.
- Eliminate the original Type declaration.
- Ensure that objects are allocated when needed:
- Simple variables: Where "Dim var As TypeName?" exists, add "Set var = new TypeName?".
- Arrays: Ensure that array elements are given new objects at initialization, or when first used.
Fair Warning: I haven't done this yet. I'll try it out as soon as I get a chance. Comments are welcome. -- JeffGrigg
I would suggest changing 2 to use public members and postpone 3 until you need it. Property gets and lets are more work and CodeClutter?. VisualBasic types are read/write without any checks, so the possibility of YouArentGonnaNeedIt is definitely there.
-- ThomasEyde
In practice we did make all member variables public. In fact, we started with property get/let functions, but later killed practically all of them, making the member variables public. (Not that the project was an example of good, or even acceptable OO practice. ;-) I would think that "good" objects would have well designed methods and no public properties. (That is, instead of refactoring between public member variables and public properties, it would be better to ExtractMethod into the class, to create proper initialization functions and methods, and eliminate public properties.) -- JeffGrigg
Some other things to look out for if you're working with legacy code:
- Fixed length strings (declared as String * <n>) are not allowed as public members of a class. Don't immediately turn them into variable length strings, keep the fixed length string as the private member and add the appropriate property procedures. The code may depend on some of the side effects of using fixed length strings (e.g. automatic padding/truncation)
- Similarly fixed length arrays within a Type must be handled as Property procedures, with an additional Index parameter. They can assign and read from a private array.
Type
GrilRec(2) as Long
becomes
Private clGrilRec(2) as Long
Public Property Get GrilRec(ByVal Index As Long) As Long
GrilRec = clGrilRec(Index)
End Property
Public Property Let GrilRec(ByVal Index As Long, ByVal NewGrilRec As Long)
clGrilRec(Index) = NewGrilRec
End Property
- Basic Type members (long, string etc.) can be passed to procedures ByRef, and modified within them. Public members of a class can't be modified in this way, because they are treated internally as being Property procedures which return a value, not the actual variable. This can be tricky to hunt down.
- Using Input # to read a member from a file will not work, because it requires a reference to a "real" variable. The compiler will catch this.
- UDTs can be written to and read from a file in one block, using Get and Put. Class objects cannot.
--
IainBuckingham
Two insights into the relationship between Types and Classes in VisualBasic:
- If the Class wraps a a private Type variable ( the class's state), it may provide methods to implement IO.
- Object composition is sometimes simpler if local Types are used instead of lots of Classes. ( See: David R. Hanson * C Interfaces and Implementations )
I have changed step 6 where it suggested to convert to "Dim var As New TypeName?", which should only be useful vary rarely. -- MarkHurd
CategoryRefactoring CategoryVbClassic