Designing a framework is not a small undertaking. There are many considerations to be addressed by the design. Using an effective design can mean the difference between success or failure on a development project.
In this session we will discuss the major points and issues related to the design of an application framework. The high points are listed below.
One of the major problems encountered in object oriented design is being so caught up in the detail of implementation that the whole picture cannot be seen. This session will circumvent this problem by NOT discussing the details of implementation, but will instead focus on the abstract design and the issues involved with that design.
Issues and items which will not be discussed are;
Regardless to whether you are building your own framework or evaluating one of the many commercial frameworks available, you need to know your requirements clearly if you want any chance of being successful.
Below is a list of questions you need to answer in order to begin identifying you requirements.
Since the framework supplies the skeleton for your application, the type of application will influence the way the framework should be structured. There are many different types of applications that Visual FoxPro can be used to build including accounting, decision support, line of business, research, marketing, scheduling, and others.
Each of these application types would require a different type of interaction with the user. Each type of application would have its own major requirements on the framework and the desired behavior for one may be a detriment to another. If you are building a decision support application the requirement for managing data saving and editing versus viewing modes for forms may just get in your way. In this case the framework that fits best would be one that was designed for decision support systems and not for accounting or line of business systems.
A framework can be designed to meet the requirements of more than one of these application types, but that needs to be specifically part of the design of the framework as well.
What Kind Of Data Sources Do You Require?
If your framework is going to be helpful in managing the mundane tasks associated with data management then it has to handle the types of data you will be using. You may be using one or more of the following data sources in your applications; local DBFs, local views, remote views, or Internet/Intranet data sources.
A framework that is the "kitchen sink" will become overly burdensome very quickly if you only use local DBF tables for your data source. However, if you use the Internet and remote views then a framework built to handle only local tables will fall way short of meeting your needs.
How Do You Prefer To Use Visual FoxPro?
Frameworks are comprised of many parts and these parts have requirements that are placed on the other parts of the framework and on the parts of the application also. These requirements are often called contracts.
When you select, or build, a framework there will be a method prescribed for building your forms and the other parts of your application in order to allow the framework to function correctly. For example, the framework may require that your forms are built as classes in a visual class library rather than as forms in the form designer. This may or may not be a problem for you. Perhaps you prefer to use the visual dataenvironment designer in the visual form designer, well if your framework requires forms as classes then the visual dataenvironment designer is not an option for you.
If you make heavy use of pageframes or grids in your interface then you should be sure that your framework will provide good support for these containers.
What Kind Of User Interface Do You Use?
User interfaces come in all sizes and shapes, mostly bad. Let's put that point to the side, this presentation is about frameworks and not user interfaces.
Your applications may present themselves to your users in one of a variety of ways. There are process centric, data centric, and goal centric approaches to this presentation. Process centric applications focus the user on the processes that are done like creating invoices, or applying cash receipts. Data centric applications orient the user to the data they work with, these types of applications ask the user to specify the file they are interested in and then let the user manipulate or edit the records in the chosen file. Finally, goal centric applications focus the user on their goals and present activities that are goal directed.
Your framework will very likely be in your way if it is data centric while you are building a goal centric application.
Imagine you just bought a new car and you are ready to drive it off the car lot. You get in, put the key in, start the car and press on the gas. As you leave the lot you turn the wheel to the right, but the car goes to the left. You stop and go back I to the salesman and ask, "Why does the car go left when I turn right"? He says, oh that is a feature of this car, you see it sensed there was a lot of traffic on the right so it went left for you instead. Not a very good car!
Well if every time you wanted to present your user with a certain set of options you first had to remove all of the options that the framework put in for you, you would soon become just as irritated with the framework.
You need to determine what the framework is responsible for and what the developer is responsible for. Don't use or build a framework that makes the developer work harder to get the result they want.
If you do build your own framework and you want to provide additional functionality beyond the framework's responsibility, then build a developer's toolkit as an addition to the framework. That way the developer can decide to use your toolkit or not as they choose and they are not bound by your style or approach.
The overall structure of the framework must be easy to understand. A well designed framework can be taught to a new developer in a few days at most. Obviously, the details of all of the methods and properties involved are not taught in a few days, but the new developer should be able to understand how the thing works very quickly and then fill in the details as they use it.
This simplicity is achieved by giving the parts of the framework clear and consistent interfaces. All objects of the same lineage should have the same interface. For example if a local data form has a method named PackTables, so should the remote data form even though the method would have no purpose in this case. Why? Because by all forms having the same methods we can interchange the controls in the forms without concern. If we had a delete button that had code to delete a record and then call the form's PackTables method this button could be safely used in both the local and remote data form because both form's have a PackTables method. In the remote data form the PackTables method safely does nothing, but there is no error when the button calls it.
Clarity
The behavioral aspects of the framework must be encapsulated. It should not be necessary for the developer to know any of the details about how the framework does something in order for them to use the framework. If the developer must understand the actual code in the framework then there is a definite design problem in that framework.
The public interface for the classes in the framework should be as simple as possible. Since it is the public interface that the developer will be working with it is important to keep no more complex than is necessary to achieve the desired functionality. The developer should not have to sequentially call three or four methods in the framework to accomplish a certain process, unless that process is often used in part.
Boundaries
The single most common problem with frameworks that I have seen is the lack, or violation, of the boundaries of the framework. The framework has clear and succinct responsibilities, it should meet those requirements and nothing more. All functionality outside the frameworks boundaries should be handled by the developer. When a framework crosses the boundary it becomes overly complex and it is likely that the developer trying to use it will need to commonly code around the framework's behaviors in order to accomplish their goals.
The framework does NOT provide the functionality for the application, it provides the skeleton upon which that functionality is built. The application functionality is the responsibility of the developer who uses the framework and the framework should not get in their way.
If a framework developer wants to provide further specialized classes for the application developer to use, these should be supplied in separate class libraries as subclasses of the framework classes. This will allow any developer to choose to use or not use these specialized classes. It also makes a clear distinction between the framework and the developer's toolkit. Coincidentally, this also gives the developer clear examples of how to subclass the framework classes to get the specialized behavior that the developer might want thus making it easier for the developer to learn the framework.
Expandability
It should be easy for the developer to expand the framework by either adding new classes or by subclassing the existing classes. If subclassing the framework classes is a difficult activity, then the framework is of questionable value. A framework whose behavior is not easily modified restricts the developer rather than empowering them.
The only sure way that a framework can provide expandability by including it in the design. Expandability is not an afterthought.
Hooks, hooks, and more hooks
One way to provide for expandability is by providing the developer with methods in which they can write code that will affect the behavior of the framework. For example, if the framework has a form that will save the users edit in a method named SaveWork, then adding a BeforeSaveWork and AfterSaveWork method which are called by the SaveWork can provide the developer with places to write code before the users edit is saved and after the save has been completed. Further, if the SaveWork method respects the value returned from the BeforeSaveWork method the developer can actually decide to stop the saving without ever looking at the code in the framework class, they can simply return .F. from the BeforeSaveWork method.
These kinds of hooks can, and should, be provided wherever they make sense.
The class design must provide a clear boundary representing the end of the framework and the beginning of the application. The diagram below shows an example using a set of form classes, the italic text indicates that those classes are abstract classes that will not be used to create objects but rather are used to be subclassed.