Getting applications up and running in the shortest amount of time is critical these days. This involved reducing the learning curve and implementing many concrete and final classes. The following paragraphs examine the importance of framework design.
Frameworks reuse both design and code. Aspects of a design, such as the kinds of objects, are easily described by code. Other aspects are not described well by code, such as invariants maintained by objects in an ensemble. The fact that aspects of a framework are not expressed well as code makes frameworks harder to understand than abstract classes. A design must emphasize its details of a at the expense of others. A framework concentrates on describing object that make up the program and how they interact.
Dataflow is de-emphasized, but the communication between object is emphasized. Since frameworks provide abstract algorithms, users of a framework usually ignore the details of these algorithms and concentrate on designing and combining objects. The concept of a framework is harder to understand than that of an abstract class. Abstract classes can be defined either by how to recognize them or how to use them. Unfortunately, there is no simple way to tell whether a set of classes is a framework, so frameworks tend to be defined on how they are used.
The important classes in a framework, such as of Model/View/Controller, are usually abstract. Like Model/View/Controller, a framework usually comes with certain concrete subclasses of the classes in the framework. No component library is perfect to derive new concrete subclasses of the abstract classes in the framework. A good component library is valuable in addition to the framework, the essence of a framework is not the component library, but the model of interaction and control flow among the objects.
Frameworks are informally defined early as a set of objects and how they interact. Most the object oriented frameworks are specified by informal documentation and a set of (usually) abstract classes that represent the objects. The classes give the operational specification of how objects in the framework work together.
Given a specification language for describing the behavior of a set of objects, a framework would be a function from the set to constrain the behavior. For example, the Model/View/Controller framework would be a function from the three objects in a MVC trio: the view has a function from the state of the model to an image to invariants like the image on the screen inside a view’s bounding box is its function applied to the state of the model, and to operation sequences like whenever the controller changes the state of the model, it performs the operation on the view, which will redraw the screen. Note that a framework can define both the invariant and the outline of the algorithm.
Frameworks rely on undefined properties of the objects, such as the function from the state of the model to the image. A framework is filled out by selecting objects according to how they define these properties. The objects in a framework often place additional constraints on each other. Views (controllers) often use specialized operations to read (change) the state of a model. The major problem with expressing frameworks in a programming language is that it is hard to learn the constraints that the framework imposes on a program. The major advantage of expressing a framework as a program is that the algorithms and data structure of the program are automatically reused by every instantiation of the framework. There is usually an abstract class for each component in the framework. The algorithms and data structures that are reused are usually defined by these abstract classes. Each subclass of the abstract class defines a kind of component that fits into the framework, and they inherit much of their implementation from their abstract superclasses. This makes it easier to develop a library of components that can be mixed and matched within a framework.
A framework would be described both operationally and in terms of the constraints that it places on its components. This would not only provide code reuse, but make it easier to learn how objects in the framework interact. A framework reuses analysis, design, and code. It reuses analysis because it describes the kinds of objects that are important and how a large problem should be broken down into smaller problems. It reuses design because it contains abstract algorithms and describes the component interfaces that the programmer must implement and the constraints that any implementation must satisfy. It reuses code because it makes it easy to develop a library of compatible components and because the implementation of a new component can inherit most of its implementation from an abstract superclass. All of these are important, though in the long run it is probably the analysis and design reuse that provide the biggest payoff.
A framework hides the parts of the design that are common to all instances, and makes explicit the pieces that need to be customized.
A framework is most valuable when the part of the design that is hidden is the part that programmers find hardest to understand.
A class that is not abstract is concrete. In general, it is better to inherit from an abstract class than from a concrete class. A concrete class must provide a definition for its data representation, and some subclasses will need a different representation. Since an abstract class does not have to provide a data representation, future subclasses can use any representation without fear of conflicting with the one that they inherited. Creating new abstract classes is very important, but is not easy. It is always easier to reuse a nicely packaged abstraction than to invent it.
A programmer usually tries to create a new class by making it a subclass of existing class, since this is less work than creating a class from scratch. This can result in a class hierarchy whose topmost class is concrete. The top of a large class hierarchy should almost always be an abstract class. As a programmer gains experience, they will then try to reorganize the class hierarchy and find the abstract class hidden in a concrete class. The result will be a new abstract class that can be reused many times in the future.
An example of a class that needs to be reorganized is View, which defines a user interface object that controls a region of the screen. A view might have many subclasses, but is concrete. A careful examination will reveal a number of assumptions made in a view that most of its subclasses do not use. The most important is that each view will have subviews. In fact, most subclasses of a view implement views that can never have subviews. Quite a bit of code in a view deals with adding and positioning subviews, making it very difficult for a beginning programmer to understand the key abstractions that a view represents. The solution is simple: split View into two classes, one (view) of which is the abstract superclass and the other (view WithSubview) of which is a concrete subclass that implements the ability to have subviews. The result is much easier to understand and to reuse.