Mega Code Archive

 
Categories / Delphi / Ide Indy
 

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.