
Newbie Q on OOP design concepts
Quote:
> I am just starting to use OOP, migrating from a legacy VAX/VMS
> system to VB 3.0 to VB 4.0. I realize that 4.0 isn't truly object
> oriented, however, it has many of the same concepts (ie classes,
> instancing, etc) and I am having some trouble designing the objects.
> Lets say I have a "customer" class. This customer object will contain
> (VB 4.0 uses "containment" rather than "inheritance" concepts) other
> classes - a "general info" class, an "A/R" class, a "line item history" class,
> etc.
> My question is - should the customer class do the work of of filling the properties
> of the other classes, or should the other classes do the work and the customer
> class calls methods in the other classes to accomplish this?
> More generally, should it be designed so the you will never have a "general info" class
> without a customer class, or should it be designed so that if you don't want all of the
> other classes of the customer (A/R etc) and just want general info, you can reference
> the general info class without having a customer class?
> Even more generally, in OOP is it good practice to make every class that you
> create independent and creatable from anywhere, or is it acceptable to have classes
> dependent on other classes?
> Thanks for any help you can provide.
> Gordon Lawson, Senior Software Engineer
> Computers Unlimited
> Billings, MT
> (406) 255-9500
Good questions. I ran into a few of those myself.
In a nutshell, there are no hard and fast rules. However,
in regards to your first question, if you have a class that
contains an object of another class, the container should
not get and set the contained class's properties; it should
expose the contained class as a property, and let the user
set them himself. Like so:
'// Class CMyClass
Private pm_objChild As New CMyOtherClass
Public Property Get Child() As CMyOtherClass
Set Child = pm_objChild
End Property
Using this technique, your users write code like this:
Dim obj As New CMyClass
obj.Child.Property = somevalue
On the other hand, you can simulate inheritance as follows:
'// Class CMyClass
Private pm_objBaseClass As New CMyBaseClass
Public Property Get m_strName() As String
m_strName = pm_objBaseClass.m_strName
End Property
Note that the "base class" object is NEVER exposed to the
user directly; the user gets to it through the "derived"
class. In this way, the derived class seems to have truly
inherited all the properties and methods of the base class.
In addition, you can simulate virtual functions by simply
redefining a base class method in a derived class.
This can be tedious, but it provides an excellent mechanism
for reuse. I am in the process of writing an AppWizard and
ClassWizard that will make this kind of simulation far
easier to implement. These tools will automatically create
such "derived" classes for you, eliminating 99% of the work.
As to whether or not you want every object to be creatable
from outside of your application, my opinion is this:
definitely not. Visual Basic sorely lacks constructors, which
allow users to say not only "Create an object of this type,"
but also "Create an object of this type and initialize it to
these known values." The Class_Initialize event doesn't even
come close to meeting these needs. I have run smack into this
dilemna repeatedly.
Fortunately, there is a solution. First, do not make all your
classes creatable from outside the project. You only want to
make one class creatable; make it a generic top-level class
like CObject or something. In that class, expose a method
that creates objects of your other classes. Let those methods
take arguments; that way, you can create a "constructor" for
your classes.
To illustrate this point, consider a CRect class that takes
X, Y, CX, and CY properties. In C++, you can create a CRect
in any of the following ways:
CRect rect;
CRect* rect = new CRect(rectMyOtherCRect);
CRect* rect = new CRect(0, 0, 120, 120);
In VB, we get this:
Dim rect As New CRect
And that's it. However, if you create a base (top-level)
class, you can place your constructors in it, like this:
'// Class CObject
Public Function CreateCRect(ParamArray args()) As CRect
Dim rect As New CRect
On Error Resume Next
If IsMissing(args) Then
' Equivalent to a C++ default constructor
' Don't do anything
Else
Select Case ubound(args) - lbound(args) + 1
Case 1
' Create a new CRect, and use an existing
' CRect to initialize it.
rect.X = args(0).X
rect.Y = args(0).Y
rect.CX = args(0).CX
rect.CY = args(0).CY
Case 4
' Create a new CRect, using the provided
' dimensions
rect.X = args(0)
rect.Y = args(1)
rect.X2 = args(2)
rect.Y2 = args(3)
Case Else
' Invalid construction
Err.Raise vbObjectErr + 1, , _
"Invalid arguments"
End Select
End If
Set CreateCRect = rect
End Function
Hope all this helps!!
Mike Hofer