Mega Code Archive
Designing for Reuse
Title: Designing for Reuse
Question: Designing for reuse is one of the more difficult programming tasks around. Here are ten guidelines to keep in mind when creating new classes that will make your new classes better equipped to be reusable.
Answer:
1. Use Abstract Classes
Abstract classes are classes that serve as a foundation for others, but are never meant to become objects in an application. Abstract methods are powerful ways of reducing the amount of code you write, resulting in very clean descendant class code. You can use an abstract class to implement the Template Method pattern discussed at the beginning of this paper. Abstract classes often appear at splits in the class hierarchy. For example, if you need checkbox and radio button components with text-wrapping capabilities, you could create an abstract class to handle word wrapping as well as provide focus handling, keyboard support, mouse support, and component painting support. Descendant classes provide just the additional functionality in an often surprisingly small amount of code.
2. Avoid Abstract Methods
Sometimes abstract classes declare abstract methods, which require that they be overridden by any descendant classes. If you forget to override an abstract method and the abstract method gets called, Delphi will terminate the application. For this reason I recommend that you steer clear of abstract methods, and instead declare your methods as virtual or dynamic, implementing empty method stubs in your parent class.
3. Provide Protected Properties and Events
Making properties and events protected is the easiest way to start designing for reuse. This is applicable if you have a property or event that may be rarely used, or if you have a group of properties in an abstract class (and you want descendant classes to decide what properties to expose).
4. Make Methods Protected Instead of Private
Protected methods allow descendant classes to invoke functionality provided by the parent class. If you want to make a method private, you should consider making it protected instead. This will allow descendant classes to call the method if needed. A good rule is to make the method protected unless theres a very good reason to make it private.
5. Tie Virtual Methods to Component State Changes
Virtual methods also allow descendant classes to hook into component state changes. In this case, the virtual method call is initiated by the ancestor class (as in the Template Method pattern). You should declare virtual protected methods for every major state change that your component performs. For example, if you have a composite component consisting of a button and an edit field, you should implement at least two virtual methods: ButtonClick and Changed, so that descendant classes can hook into these events. If the button presents a dialog, you may want to offer the results of that dialog to another virtual protected method that is called when the dialog is OKd.
6. Use Virtual Methods to Trigger Events
Events are a prime example of component state change. Event triggers should be placed in virtual (or dynamic) protected methods, and look like this:
procedure TDBSlideBar.TriggerPostDataEvent;
begin
if assigned(FOnPostData) then
FOnPostData(Self);
end;
7. Avoid Nested Procedures and References to Private Parts In Virtual Methods
Private methods and private data are not available to descendant classes (unless the classes are placed in the same unit as the ancestor class not recommended). Avoid calling private methods or accessing private data from within a virtual or dynamic method. Make the private methods and/or data protected instead. Avoid placing nested procedures inside a virtual or dynamic method. For a good example of what not to do, see TStringGrids DrawCell method in Grids.pas of the VCL.
8. Make Public Methods that Change State Virtual (or Dynamic)
Public methods that result in state changes to your class should be virtual or dynamic. A good example is a dialog components Execute method. This allows descendants to alter and/or hook into important functionality.
9. Document How the Class is Designed for Reuse
Document the protected properties, events, and methods so that your component "reusers" can more readily understand what they can do in a descendant class. For example, when you create a virtual or dynamic method, be sure to document when a designer might override the method. This can obviously run the gamut from always to seldom.
10. Descend from your Class to Test its Applicability for Reuse
Rarely is a first design properly built for reuse. Just like an application must be tested before it ships, class designs must be tested for reuse before they are passed on to others as "reusable". The only way to test a class design for reuse is to inherit from it, creating a new class with augmented functionality. If you work in a team environment, hand the component to someone else in the team and have them create a descendant class based on yours, with additional functionality.