The cores of everyone's development efforts are classes. You've got down the basics of creating methods in your objects, you know how to keep them lightweight, and you're familiar with all of the syntax necessary to implement your ideas. However, what do you do when you have to think about how your object lives and breathes internally, not what it exposes to the world?
Internal vs. External
The way that an object exposes itself and the way that it behaves internally, can and should be different. Objects expose themselves in ways that makes them easy to use for the developer. They work in ways internally that hide all of the complexity from their external developer-users.
This is particularly true of lightweight objects, which behave externally like they are always fully populated while internally they only initialize the pieces of the object that they need. Take, for instance, a product object, which supports collection properties for related products, categories, attributes and vendors. Externally, the developer can access these properties at any time. Internally, the object populates the collections only when the user asks for them since each means a separate database hit. Internally, there are private fields that may or may not be initialized. Externally, the collection is always returned even if there is nothing in the collection.
Another example would be a property that represents the last user that updated the product. The internal storage might be the users' GUID. The property might, however, return a user object. In this case, the way that the object behaves internally, managing a GUID, is different than the way it behaves externally, returning a user object.
These distinctions are important when you look to develop persistence and serialization strategies. In both of these cases the way that the object is stored isn't necessarily the way that the developer uses it. What you store in a database, and what you use to transport the object, isn't the properties that the object exposes. It is the internal fields that must be populated for the object to be the object. It's the data in the object that distinguishes it from other objects. An object without data is simply the class.
Developing a persistence strategy whether to a database, text files, or some other mechanism isn't for the faint of heart. Organizations have spent years trying to get their persistence strategy right. However, there is one basic fundamental truth. Objects must persist themselves as they see themselves internally. They save and read GUIDs, identifiers, and simple fields. One to many relationships represented in the object as collections are stored in the database as sets of key values in varying tables.
Powerful persistence strategies are developed on automated means of taking all of the fields within the object and storing them. This includes all of the fields from super classes. Persistence strategies include an understanding of how the related objects are stored, however this is done.
Ideally persistence strategies will be simple to implement and difficult to make mistakes. The idea of any strategy is to make it more difficult for errors to occur through the use of procedures, shared code, and tools.
One of the other core challenges that any persistence strategy has to tackle is how to handle nested objects. Nearly every non-trivial object will be made up of objects itself. It's important to realize that nested objects must persist themselves as well. In developing the persistence strategy it's important to develop a structure that allows for underlying objects to be persisted when the upper level object is persisted.
The challenge when persisting all of the underlying objects is suppressing extra updates which don't need to happen since now every request to persist the upper level object will cause the underlying objects to be persisted as well, whether or not they have changed.
Serialization, as it happens, is very similar to persistence. In fact a good serialization should allow for that serialized object to be persisted for an arbitrary length of time. As serialization is pushed into a message queue or some other non-real time communications mechanism it does in fact become temporary persistence of an object. Because of that the mechanisms for serialization should rest upon the same persistence engine that is used to store an object in a more permanent way.
There is one minor exception to this. Occasionally serialized objects are used for other purposes. In one recent project we serialized the basic commerce objects and used XSLT to transform them into emails for the user. In this situation, serializing the internal view of the object works except for the fact that the object may or may not be completely spun up. A lightweight object may or may not have loaded all of their component parts. This may not be desirable. Sometimes it's necessary to cause an object to load all of the component parts of it self before it's serialized.
Handling Lightweight Objects
In a previous article I indicated that lightweight objects are a great way to manage performance in an object-oriented world. The challenge is all of the lightweight properties of the object won't serialize when the object is serialized. At least the properties won't be serialized when the object hasn't been fully initialized. For cases like these, your serialization strategy should include a parameter for whether an object needs to be fully initialized before it can be serialized. Further, that parameter should control whether the need to fully initialize lightweight objects should be pushed down through the hierarchy of the objects.
Normally, serialization is allowed to operate without fully initializing lightweight objects. In cases where the outer object is going to be used for non-reconstruction purposes like to generate emails from an ecommerce system it may make sense to fully initialize the outer object. In some cases, it may be necessary to fully initialize inner objects to ensure that the necessary fields are available for the email generation process.
The way that an object views itself and the way that developers view the object are and should be two different things. Hiding the complexity of the underlying operation is an important pillar of the object oriented design model. The right way to handle object persistence and serialization do cause some interesting challenges as you try to use the serialized data for other things, such as the input for XSLTs. These challenges are easily addressed if you know how to prepare.
About the Author
Robert Bogue, MCSE (NT4/W2K), MCSA, A+, Network+, Server+, I-Net+, IT Project+, E-Biz+, CDIA+ has contributed to more than 100 book projects and numerous other publishing projects. He writes on topics from networking and certification to Microsoft applications and business needs. Robert is a strategic consultant for Crowe Chizek in Indianapolis. Some of Robert's more recent books are Mobilize Yourself!: The Microsoft Guide to Mobile Technology, Server+ Training Kit, and MCSA Training Guide (70-218): Managing a Windows 2000 Network. You can reach Robert at Robert.Bogue@CroweChizek.com.