16.8. Attribute Demolition
As mentioned under "Destructors" in Chapter 15, one of the very few annoyances of using inside-out objects rather than blessed hashes is the inevitable need to write separate clean-up code for every attribute, as in Example 16-11. Example 16-11. Cleaning up object attributes package Book; use Class::Std; { This kind of highly repetitive code structure is inherently error-prone to set up, unbearably tedious to read, and unnecessarily hard to maintain. For example, are you confident that the DEMOLISH( ) method shown in Example 16-11 actually did clean up every one of the object's attributes? The goal here is always exactly the same: to iterate through every attribute hash in the class and delete the $ident entry inside it. It would be much better if there were some way for the class itself to keep track of its attribute hashes, so the class itself could automatically step through those attributes and remove the appropriate element from each. Of course, you could do that "manually", by creating an array of references to the class's attribute hashes and then iterating that array with a for loop. For example: package Book; { But then you'd need to write essentially the same DEMOLISH( ) in every class. The code for declaring and collecting the attributes is pretty scary, too. The Class::Std module provides a simpler way to accomplish precisely the same goal. It provides a "marker" (:ATTR) that can be appended to the declaration of each attribute hash[*]. Whenever that marker is used, Class::Std stores a reference to the marked hash and then automatically applies the appropriate delete call after the class's DEMOLISH( ) method has been called. So the previous code could be rewrittenwith exactly the same functionalitylike so:
package Book; use Class::Std; { my %title_of :ATTR; my %author_of :ATTR; my %publisher_of :ATTR; my %year_of :ATTR; my %topic_of :ATTR; my %style_of :ATTR; my %price_of :ATTR; my %rating_of :ATTR; my %sales_of :ATTR; With this version, the necessary attribute-hash deletions would be performed automatically, immediately after the universal destructor's call to DEMOLISH( ) was finished. And if the class didn't define a DEMOLISH( ) method at all, the destructor would still perform the deletions at the appropriate time. Note too that the # Attributes... comment has been omitted from this second version; the column of :ATTR markers is sufficient documentation. As a final improvement to maintainability, a single :ATTR marker (or its synonym, :ATTRS) can also be applied to an entire list of attribute-hash definitions. So the previous code could be further reduced to: package Book; use Class::Std; { my ( %title_of, %author_of, %publisher_of, %year_of, %topic_of, %style_of, %price_of, %rating_of , %sales_of, ) :ATTRS; |