2.2 Marco Teórico
2.2.4 Asignatura de Matemáticas
Think of a class library as normalized code. Just as each non-key field in a database is unique to that database, each property or method should be unique to the class library. A general rule of thumb is that if you are changing behavior, and have to do it in more than one place, you have done something wrong. How you approach the organization of each class line differs depending on the classes under consideration. The results, however, are the same: classes related in such a way that only one implementation of each feature exists.
I mentioned that building a framework is an iterative process. The same is true of structuring your class library. This is your first iteration. Focus on inheritance and the
relationship between classes. Do not rush to add every feature you want as part of your library in this pass. Instead, work deliberately and build up your class library in layers. The way you
60 Build Your Own Framework with Visual FoxPro
implement a specific feature in a framework may not be the same as if you were implementing the feature in isolation.
The requirements for the visual controls will state that each control must respect the state of the form: edit mode, read-only, styles, and so on; and any security settings. Because you haven’t created any forms yet or implemented the security module, it’s impossible to implement how controls integrate with forms or security at this point in your development.
During the beginning stages of development, your only concern is to define the major roles each class will play and to add functionality that is isolated to the specific class. The only relationship between classes you should be concerned with at this time is the inheritance relationship. Stated differently, you are defining the classes you need and their lines of inheritance, and implementing some basic functionality.
Understand the roles of your classes. The base classes contain common framework code.
If you intend to specialize a class beyond the common code, create a subclass.
How classes integrate with other classes is not your concern … on this pass. And that’s a key point. This is just one pass. Don’t create a hierarchy 20 levels deep. Keep it shallow and broad at the start. In subsequent passes, you’ll have the chance to extend and specialize.
Approach the “normalization process” starting with the most general and working toward the most specialized, and then again from specialized to general. Your base classes are general classes. For example, the Textbox class contains certain functionality that is common to all text boxes. Users see a text box and expect to be able to enter information into it. That is a common characteristic of text boxes, regardless of the type of data displayed. A specialized text box might be one that formats numerical data similar to a calculator, with numbers greater than 0 scrolling from the right. This feature is not suitable for all text boxes; it is a feature for a special text box.
Next, repeat the process in the reverse order. In other words, work back through your class structure from the specialized classes to the most general class. This time you are looking for common features that could be moved up in the class structure.
In practice, the approach you start with is a matter of preference and circumstance. The key point is to make a minimum of two passes. The best way to illustrate these concepts is through examples. I’ll start with labels.
Labels
Labels display information throughout an application. Labels can also convey information to developers; these labels never appear in the completed application. For example, I often use a Label class rather than a Custom class because I can use the Caption property to display some meaningful information. For example, in Chapter 8, the Framework collection classes are based on the label. When I add a label to a form, I can use the caption to indicate why the collection is on the form.
In these situations, I want the caption to appear during development, and not in the final application. An invisible label (invisible to the user) with automatic word wrap is perfect for a purpose such as this.
First pass
We have identified that labels can be captions on a form or they can be a utility for developers.
Consider each purpose. Can the caption be specialized further? Sure. I usually display a title on each form and occasionally a subtitle or two. And each form control usually has a prompt associated with it. Okay, I’ve identified three additional potential classes.
Chapter 6: Creating a Class Library 61
Repeat the process. Can a title be further specialized? Depending on your situation, sure.
However, your job as a framework developer is to provide a class structure that can accommodate as many styles as are needed by the application developer—not to define the styles themselves. Be careful not to create classes “in case” you need them. Without a clear reason to provide specially formatted labels, stop the specialization process here, for the same reasons the SubTitle and Prompt classes are also not extended further.
Evaluate the use of labels as a developer’s utility. Can the Label Utility class be further specialized? Sure, and I’ll expand on that later. For now, let’s just create the aUtilitylbl class.
Okay, we have identified the following classes of labels: Caption, Title, SubTitle, Prompt, and Utility. Ask yourself if each class is useful on its own or if it is provided as a starting point for others. Title, SubTitle, and Prompt are useful in their own right and are preceded by the framework prefix of “my.”
The other classes are abstract classes meant to be further specialized (subclassed) before use. According to the MyFrame standard, they are prefixed with an “a.” The resulting class hierarchy is shown in Figure 3.
Figure 3. The Label class hierarchy.
Second pass
Reverse the process used in the first pass and evaluate each class, starting with the most specific classes and working back to the most general. When working from specific to general, look for commonalities between classes. If a property or event exists in two classes, move the definition of that class or property up to the parent.
Start with myTitleLbl. Is there any behavior you want for myTitleLbl? Yes, as a matter of preference, my titles have the following settings:
BackStyle = 0 && Transparent FontSize = 14
FontBold = .T.
AutoSize = .T.
Move on to the next class, the SubTitle. Is there any behavior you want for all subtitles in the framework? Yes, and they are:
BackStyle = 0 &&Transparent FontSize = 11
Fontbold = .T.
AutoSize = .T.
62 Build Your Own Framework with Visual FoxPro
Well, wait a minute. The only differentiation between these classes is the font size. Rather than setting each property (BackStyle, FontBold, and AutoSize) in each of the classes, set the value for these properties once in the class aCaptionLbl. In other words, do not set the
BackStyle property to 0 in the class myTitleLbl; let it inherit the value of that property from its parent class. Setting a property once at the parent-class level allows the application developer to change properties in aCaptionLbl and have those changes reflected in all classes based on aCaptionLbl. Are there any other changes we want to make to aCaptionLbl? Not at this time.
Before moving up to aLabel, evaluate other classes on the same level as aCaptionLbl. In this example, only one class exists on this level: aUtilityLbl.
Are there any specialized characteristics we want for aUtilityLbl? Yes, the user should not see this control. So you have a choice here. You could simply set the Visible property to False (.F.), allowing the developer to see it only at design time, and requiring the developer to change the property to .T. if he or she wants to see the control on a form. Alternatively, you could use a bit of code to conditionally allow the developer to see the control.
IIF(VARTYPE(goApp)='O' AND Pemstatus(goApp,'ShowDevTools',5), goApp.ShowDevTools(),.F.)
The application object has a public property, lDevMode, that could be used as the condition for showing developer tools. However, running in development mode is not the same thing as wanting to see developer tools. A better approach is to add a new method, ShowDevTools(), to the aApplication class defined in Chapter 5. The default behavior of
ShowDevTools() is to return lDevMode. By providing a separate method to retrieve this value, the application developer can overwrite this method as desired in the application subclass pApplication.
Are there any other characteristics that aUtilityLbl should have? Yes, AutoSize should be set to True (.T.) and BackStyle to 0 (Transparent). Now that all classes have been evaluated at this level in the class hierarchy, move up to the parent aLabel.
aUtilityLbl and aCaptionLbl have two properties, AutoSize and BackStyle, that are the same for each class. aUtiltiyLbl and aCaptionLbl represent all the classes directly subclassed from aLabel. Evaluate whether you want to change the parent class, aLabel, rather than set AutoSize and BackStyle directly in each of the subclasses. In this case you want to move the setting of these properties to aLabel.
The Label class is a good example of how you define the inheritance relationship between classes. Several properties (FontBold, AutoSize, BackColor) appeared to belong to one class, but because of inheritance were best implemented in another class.
Chapter 6: Creating a Class Library 63