A framework is a set of cooperating classes that make up a reusable design for a specific class of software.
The framework dictates the architecture of your application. It will define the overall structure, its
partitioning into classes and objects, the key responsibilities thereof, how the classes and objects collaborate, and the thread of control. A framework predefines these design parameters so that you, the application designer/implementer, can concentrate on the specifics of your application. The framework captures the design decisions that are common to its application domain. Frameworks thus emphasize design reuse over code reuse, though a framework will usually include concrete subclasses you can put to work immediately.
Reuse on this level leads to an inversion of control between the application and the software on which it’s
based. When you use a toolkit (or a conventional subroutine library for that matter), you write the main body
of the application and call the code you want to reuse. When you use a framework, you reuse the main body
and write the code it calls. You’ll have to write operations with particular names and calling conventions, but
that reduces the design decisions you have to make.
Not only can you build applications faster as a result, but the applications have similar structures. They are
easier to maintain, and they seem more consistent to their users. On the other hand, you lose some creative
freedom, since many design decisions have been made for you.
If applications are hard to design, and toolkits are harder, then frameworks are hardest of all. A framework
designer gambles that one architecture will work for all applications in the domain. Any substantive change
to the framework’s design would reduce its benefits considerably, since the framework’s main contribution to
an application is the architecture it defines. Therefore it’s imperative to design the framework to be as
flexible and extensible as possible.
Furthermore, because applications are so dependent on the framework for their design, they are particularly
sensitive to changes in framework interfaces. As a framework evolves, applications have to evolve with it.
That makes loose coupling all the more important; otherwise even a minor change to the framework will
have major repercussions.