Visual Basic 6 Business Objects
These changes are easy enough to test by slightly breaking the code in our EditPerson form. Just comment out the call to BeginEdit that we put in the Form_Load routine and then run the program.
Now, any attempt to change values in the form's fields will result in error 383 being raised. Clicking the Cancel button should result in error 445.
When you've finished this test, don't forget to uncomment the call to BeginEdit!
While a well-behaved form will never actually encounter these errors, they are vitally important during development of the UI. Again, our objects must be written with the assumption that the UI developer is not going to write the perfect set of code on the first pass, so what we're doing here is helping the UI developer do their job while protecting our objects from errant code.
Protecting Properties and Methods
In the previous section, we effectively switched some properties from read-write to read-only, based on whether the object is flagged as editable. We also disabled some methods using a similar technique.
In this section, we'll quickly run through the reasons why we might want to disable properties or methods. We'll also consider exactly how to implement this disabling.
There are various reasons why we might need a read-only property. The most common is where we have a property that's calculated from other data within the object. A good example of this is the Age property in our Person object, which is read-only and is based on the Birthdate.
Other properties might switch between read-write and read-only, depending upon business rules or other criteria. We saw an example of this behavior in the previous section, where we made properties read-only when the object was not in an editable state.
There are two techniques available to us for creating read-only properties. The simplest technique is to provide no Property Let or Property Set routines for the property. Of course, this technique allows no flexibility, in that we can't then make the property read-write at runtime. The second technique available to us is to still create the Property Let or Property Set routines, but to raise error 383 'Set not supported (read-only property)' at the top of the routine when we want to make the property read-only:
Public Property Let X(Value As Integer) If condition_met Then Err.Raise 383 Regular code goes here End Property
As we've seen, there are situations where we need to temporarily disable methods, Sub, or Function code within our object. Depending on the state of the object, or various business rules, we may need to effectively turn off a method.
Disabling a method is as simple as generating error 445 'Object doesn't support this action' at the top of a method's code when we want that method disabled:
Public Sub X() If condition_met Then Err.Raise 445 Regular code goes here End Sub
We did this earlier, in the ApplyEdit and CancelEdit methods of our Person class. They weren't appropriate unless the object was currently being edited and the flgEditing flag was set to True.
There are also cases where we may wish to create a property that is only written once. This is an excellent technique to use for unique key values that identify an object, and for those situations where business rules dictate that data can not be changed once entered. Real-world examples of this would include an electronic signature or an identity stamp where an object is stamped with a security code that must not be changed.
Write-once properties are implemented using the same error raising technique as a read-only property, but with a bit more logic to support the concept. As an example, let's enhance our Person object's SSN property to be write-once. After all, once a person gets assigned a social security number, they've got it for life.
Indicating when the Object is 'New'
We can't consider the SSN to be entered until the object is first saved by the user when they click the OK or Apply buttons. At that point, we need to lock down the Property Let SSN and any other write-once properties.
To lock down the value, we'll add a module-level variable to our Person class module to indicate whether the Person object is 'new'. This same variable will come in useful, later, when we talk about saving and restoring objects from a database, since a restored object also needs its write-once properties locked out. Add this variable declaration to the General Declarations section of our Person class module:
Private flgNew As Boolean
Since the UI might also care to know if an object is new or not (so it can alter its appearance appropriately), we'll also create a read-only property for this purpose by adding the following code to the Person object:
Public Property Get IsNew() As Boolean IsNew = flgNew End Property
Now let's implement the flgNew variable's operation. We need to start out assuming the object is indeed new:
Private Sub Class_Initialize() flgEditing = False flgNew = True Set objValid = New BrokenRules objValid.RuleBroken "SSN", True End Sub
Then we just need to flip the switch, when the Person object is saved, via the ApplyEdit method:
Public Sub ApplyEdit() If Not flgEditing Then Err.Raise 445 flgEditing = False flgNew = False ' data would be saved here End Sub
Disabling the Property
All that remains is to change the Property Let SSN routine to become disabled when the object is no longer considered new:
Public Property Let SSN(Value As String) If Not flgEditing Then Err.Raise 383 If Not flgNew Then Err.Raise 383 If Len(Value) > 11 Then _ Err.Raise vbObjectError + 1001, "Person", "SSN too long" udtPerson.SSN = Value objValid.RuleBroken "SSN", (Len(Trim$(udtPerson.SSN)) <> 11) End Property
With these changes, the SSN can be entered and changed by the user up to the point where they click OK or Apply. At that point, the Person object will be saved to the database, the flgNew flag would be set to False, and the user will no longer be able to edit the SSN field.
Testing the Code
We can test this in our program, even though the data isn't actually saved to the database, since the ApplyEdit method still sets the flgNew flag to False when the Apply button is clicked.
Run the program, enter 11 characters into the SSN field, and click the Apply button. Now try to change the SSN field again. As a result of the code we've just entered, you'll find that the SSN is fixed. Once the data is saved to the database our object wont allow it to be edited.
Of course the use of write-once properties isnt without risk. After all, this technique means that the user cant change the SSN value after its been saved to the database even if it is incorrect.
Write-only properties are less common than read-only properties, but they do have their uses. A good example of a write-only property is a password property on a security object. There's no reason to read the password back, but we would, sometimes, want to set it.
As with read-only properties, there are two ways to create write-only properties. If we don't need to dynamically change the status at run-time, we can implement a write-only property by simply not writing a Property Get routine for the property.
However, if we do need to dynamically change a property from read-write to write-only for some reason, we just need to raise error 394 'Get not supported (write-only property)' at the top of the Property Get when the property needs to be write-only:
Public Property Get X() As Long If condition_met Then Err.Raise 394 Regular code goes here End Property
Making Objects Persistent
Throughout our discussion, so far, we've danced around the idea of making an object persistent. Objects are great, but if we can't save an object into a database and then retrieve it later, they're somewhat limited in their use.
As we've seen, business objects are intended to represent real-world entities. An entity in the real world, such as a customer, has no concept of saving, adding, updating, or deleting itself - those things just don't make sense. However, when we create an object in software to model a customer or product, we need to compromise the model slightly in order to handle these activities.
A primary goal in making business objects persistent is to minimize the impact to the logical business model. We want a customer object to be a model of a customer, not a model of a database record.
In this section, we'll discuss a couple of techniques that we can use to efficiently save business objects to a database without compromising the integrity of the CSLA. Then, we'll talk about what part of our program should actually do the work of saving and retrieving the data. Finally, we'll look at the details of the persistence service that we'll be using through the rest of the book.
The first thing we need to look at is exactly how are we're going to save an object to the database. Virtually all the databases used today are relational databases, although there are exceptions - such as hierarchical databases and object databases. Because of the prevalence of relational databases, however, we're going to focus on saving and restoring objects from that type of database, leaving the others to be covered elsewhere.
It's important to decide where to locate the code that takes care of saving or updating an object in the database. The specifics of how to save an object's data will depend upon where we put this code. There are three basic approaches:
- Saving/restoring is handled by user-interface code
- Business objects save/restore themselves
- A specialized object manages the saving/restoring of business objects
The first solution may be valid in certain cases, but we won't cover it in any detail, because it's directly opposed to an object-oriented solution. The second solution is very valid, but not terribly scalable. In the end, we'll settle on the last solution: it works well with our object-oriented design, and yet provides good scalability for our applications.
But let's take a good look at each of these options, and consider their pros and cons in more detail.
Saving Objects within a Form or BAS Module
In keeping with a more traditional Visual Basic development approach, it's possible to put the persistence code in the form itself, or in a BAS module called by the form. Of course, this means putting quite a bit of logic into the user-interface - something that I don't recommended, since it makes our code much less general.
With this approach, every form that wants to use a given object will need the code to save and restore that object from the database. This almost guarantees duplication of code, and largely defeats the goals that we're trying to achieve by using object-oriented design in our applications.
We're not going to go into the details of any code to support this solution, since it's so directly in conflict with the principles we're trying to follow in this book.
Page 9 of 13