September 93 - New Software Architectures
New Software Architectures
Jesse Feiler
Now it's Bedrock-or MFC, or Component Workshop, or Object COBOL. The choices for
application frameworks are increasing, and with them, the need to formulate
strategies for development and conversion of applications. New application
architectures are possible-but they carry with them costs as well as benefits: new
architectures may require different testing mechanisms, retraining of both developers
and maintenance programmers, and rethinking of source code and documentation
procedures. And, of course, new architectures carry with them a major cost-they will
become old architectures and newer ones will supplant them.
The extremes of dealing with new architectures are well-known. On the one hand,
there are examples of projects which quickly adopt all new architectures the moment
they come out-and never finish the project. On the other, there are notorious cases of
projects in which no new architectures are adopted: the code may be (grudgingly)
C++, but the file structures are all based on 80-column punch cards.
Choosing an appropriate path for a specific project need not be a matter of
happenstance or temperament. There are some very specific strategies to follow in
deciding on adoption of new architectures.
(Note: New architectures often provide new functionalities which can directly affect
the substance of an application-the TFloatWindow class in MacApp 3, for example,
which replaced a number of inelegant attempts to incorporate that functionality into
MacApp 2. This article does not address the issues of new architectures that can
provide new benefits. We are concerned with the thornier issue of new architectures
that only indirectly affect the end-user-new architectures which benefit the
programmer.)
finding and identifying new architectures
It may at first seem unnecessary to think about finding new architectures-most
developers' mailboxes are full of literature. The decisions about choosing
implementation languages and frameworks are often made with much study and due
deliberation. However, within the scope of a given framework, there are many other
design decisions involving new architectures that must be made-and must be made far
more frequently and with fewer resources.
Whether the project is a conversion from an earlier version of the same work or from
another framework or a "from scratch" application, all of the designers, engineers,
and programmers approach the work with their own bags of tricks. As part of any
project design, standards are set. These standards often suggest which features of a
framework will be used, and which will not. For example, we have become so
convinced of the power of TBehaviors in MacApp 3 that we question any override of
TView or a descendant. In almost every case the functionality that we desire can be
encapsulated in a TBehavior which is attached to the TView (or descendant) in question.
This solves many problems (including perennial problems with ViewEdit) and
encapsulates our application-specific code in objects much smaller and less
complicated than TViews.
In order to set such standards, it's important to find the new architectures. A primary
source is the promotional materials about frameworks-everything from reference
manuals to advertising, to presentations at MADACON, WWDC, and other groups. This
material has to be carefully edited: in early presentations about MacApp 3 the
gViewServer concept was stressed strongly. gViewServer is valuable, but nowhere in
the league of TBehaviors.
It's also important to keep up with the literature, including reading as many code
samples as possible. Unfortunately, programmers often don't like to read code, and
standard computer science training provides little education in reading and evaluating
code (as opposed to designing and writing it). Nevertheless, reading enough ads, code
samples, and manuals (for products you use and don't use) should alert you to trends.
Different frameworks may implement the trends differently, but you should be able to
keep your eyes and ears alert for important, common new architectures.
One final warning: be alert for "unique selling propositions" that aren't unique. We've
all been through the blood feuds of Object Pascal and C++; some people are fanning
some internecine strife over Windows and Bedrock. On close examination, competing
frameworks often prove to have remarkably similar functionalities. If framework X
provides a unique and wonderful feature (A) and framework Y provides a more-unique
and more-wonderful feature (B), at root may be some simple architecture (C)-which
is implemented in framework X, Y-and framework Z.
(Author's/Editor's note: We apologize for the use of letters in the preceding paragraph.
Identifying the frameworks and the particular "features" referred to could put our
lives in danger.)
evaluating new architectures
What Changes Does it Require?
One of the virtues of object oriented programming is that sections of programs can
easily be swapped in and out: if a new and improved object for displaying text in
various scripts arises, you can removed your old object that displays text (perhaps in
only one script) and plug the new one in.
Unfortunately, we are still at the point where many of the new architectures don't
work that simply. We are talking about new architectures, not new objects. So a new
architecture often requires modifications to a number of existing objects, and is not
simply a matter of replacing one object with another. One of the best examples of this
is the TDialogBehavior class in MacApp 3 (which, by the way, is a wonderful
improvement!). It doesn't replace any individual object from MacApp 2. Instead, by
allowing "dialog-ness" to be attached to any window, it eliminates the need for
TDialogViews. The code change to truly implement TDialogBehaviors involves removing
TDialogViews. The fact that a new architecture doesn't simply involve a one-for-one
replacement doesn't mean that there's a problem: it simply means that you have to
evaluate more issues.
In the case of TDialogBehavior, the removal of TDialogViews affects resources, and
often ripples through your application, since TDialogView almost always was
overridden. In a data-entry intensive MacApp 2 application, it was easy to come up
with half a dozen overrides of TDialogViews. All of these classes would need to be
removed, and their function-alities correctly distributed.
After evaluating the scope of the changes, you need to find something for the other side
of the balance: what benefits would adoption of this architecture provide?
Does it Save Code?
We have converted a number of programs from MacApp 1.1 to MacApp 2, and others
from MacApp 2 to MacApp 3. In each case (but particularly in the latter case) we have
found that the converted application code was shorter than the previous version. The
reason isn't hard to find. As people use the framework, many people find themselves
bumping up against limits and problems. Through a forum like MacAppTech$, the
development teams watch and listen. When a number of people have the same
problems, a new framework or new version of an old framework is likely to
incorporate not only bug fixes but framework level solutions to frequently-asked
questions and common problems.
Going back to the summer of 1990, you would discover that a hot topic in FrameWorks
and on MacAppTech$ was "What is My TDocument?" In MacApp 2, the TDocument was
the object which contained data, which could be dirtied, which could write to a file, and
which could display itself in windows. People started to have problems with documents
that weren't file based (database applications) or documents that lived in several files.
Lo and behold, the MacApp architecture for version 3 cut the Gordian knot of
TDocument, giving us TDocuments, TFiles, TFileHandlers, and dependencies for good
measure. People (like us) who had contorted databases into TDocuments found that we
could make what had been a descendant of TDocument into a descendant of TObject
(smaller, faster, simpler) and handle its dirtying with dependencies. Similarly,
applications with multiple-file documents were able to simplify their file handling
with the new structure.
In these cases, adoption of the new architectures means removing special-case code
from an application and either totally on the framework for doing the task, or
overriding a much smaller and more specific method of the framework. The work is
pushed into the framework (good) and out of the application (good).
In the case of TDialogBehaviors, cited above, the removal of TDialogViews and their
application-specific descendants also generally means a net code saving.
The virtue of saving code isn't always evident to some people. After all, we are taking
something that's not broken and fixing it (TDialogViews can still be used in MacApp 3).
The advantages of saving code are these:
• Shorter compile/link cycles.
• Easier maintenance (removing entire classes can mean removing entire
files from a project).
• Eliminating old architectures can simplify staffing requirements: there
are many "horror stories" of systems that are hard to maintain because
they've got one section still written in SNOBOL or PL/1.
• Relying on a framework to provide functionality may increase the
likelihood of that functionality being maintained across new system software
releases. (This is a hot issue right now; in the long term, there's no doubt that
this is true. Equally true is that in any given short term, it's sometimes true
and sometimes not.)
What is its Life Expectancy?
Once you have evaluated the extent of the changes required to an application by the
incorporation of a new architecture, and balanced them against the savings in code that
can be achieved, the problem is not yet solved.
Some wonderful ideas come and go–far too soon. In evaluating an architecture's life
expectancy, technical expertise seems to be less important than a knowledge of the
Tarot. Yet, it's not all mysterious.
Take as an example a small office of a dozen people with a low level of computer
literacy. Everyone knows Microsoft Word. On a few occasions a year, they need to
incorporate pictures into documents. What would you recommend?
A. Do it with rubber cement the way they do it now-and the way they did it
in the 1950's.
B. Buy a scanner and master Word's graphics commands.
C. Buy a scanner and Quark.
The correct answer, of course, is A. And it's correct not because of the low volume of
work, it's correct because with OpenDoc and OLE 2.0 it's reasonable to assume that the
way in which graphics are incorporated into documents a year from now will differ
substantially from the way in which we do it now-with any application. Now is the
wrong time for a user who can afford to wait to invest in any existing application for
this purpose, just as by reading the trade and financial pages you could find out that
buying a color printer was probably a poor investment until this year.
Often programmers, designers and system architects look down their noses at this kind
of analysis-after all, it's not technical. But, to a large extent, this type of analysis is
crucial to deciding what new architectures to adopt, which to ignore, and which to
watch.
A further example is that of TBehaviors. For a MacApp user who is not using
TBehaviors, the decision to include them in new or existing applications might well be
influenced not only by the intrinsic value of TBehaviors, but also by noting that they
are present in the interfaces to a new framework.
incorporating new architectures
Laying Out the Ground Rules
Once new architectures are selected for a project, it is essential that ground rules be
laid out. When we first started using TBehaviors, we used them in very limited ways.
Now we have no rules about their use-save that they should be used if at all possible to
avoid overrides of TView and its descendants.
TAdorners, another powerful feature of MacApp 3, also can help eliminate overrides of
TView. TAdorners are a little trickier to implement than TBehaviors, because they
require the under-lying views to make certain assumptions (since frame adorners are
drawn within views, views which might have frame adorners added to them cannot
draw to their edges in all cases).
In some cases, such as the use of the dependency mechanism in MacApp 3, the ground
rules take the opposite approach. The dependency mechanism is a wonderful feature of
MacApp 3, improving over the mostly idiosyncratic mechanisms that were added for
each application in MacApp 2. However, the dependency mechanism can break very
easily if it is not used in all cases in an application. So with regard to the dependency
mechanism, our ground rule from the start has been: only the dependency mechanism
can be used to communicate changes between and among objects.
Taking some time at the beginning to establish the parameters under which new
architectures will be used pays off. A particular benefit is that in order to establish
these parameters, it is important to truly understand the new architecture and how it
relates to the known framework.
Breaking an Application
In the case of a conversion, incorporating new architectures frequently means
"breaking" the existing application for a while. Replacing TDialogViews with
TDialogBehaviors (or descendants thereof) can take a significant amount of time.
Normally, we work with relatively small code-compile-test cycles. Breaking an
application for a period of weeks runs the risk that when it is finally put back together
again it won't compile for quite some time, or that when it is put back together again it
won't work properly.
There is no one simple answer to this problem. One thing that can help is to compile
new objects with new architectures in stand-alone applications to test their
functionality. Particularly since a new architecture may be new to programmers and
so be more bug-prone, this route may save time in the long run. In this strategy,
instead of breaking the old application, it is simply put aside, and the new objects
developed on their own. Then, when the old application is "broken" what is put in is not
new code, but entire new objects which have been tested on their own.
A further help at this stage is to rely strongly on Projector (or another such tool). If
the original application is "frozen" (i.e., all files checked in, read-only), the changes
can be made in modifiable branches that can all be discarded if necessary to return to
the status quo ante.
Testing, Documentation, and Support
New architectures challenge all members of a project team, and should be discussed
and explained to everyone. Support staff who are used to fielding help calls from users
"trapped" in a modal dialog need to start working-themselves-with software that
features movable modal dialogs with menu access.
Many of the new architectures discussed here (TBehaviors, TAdorners, dependency
mechanism, TDocument/TFileHandler/TFile structure) have the effect of moving code
from the application back into the framework. Applications override smaller objects
with fewer methods. Documentation procedures designed for applications with 50
overrides of TView and its descendants may need to be reviewed when those overrides
all are implementations of TBehaviors.
DO I HAVE TO?
Oddly enough, programmers are remarkably conservative. If you follow the
MacApp3Tech$ traffic, you can see that people are writing programs using MacApp 2
architectures-and even earlier, non-object-oriented architectures! By picking the
architectures of that will withstand the test of time, within any of the major
frameworks, applications can be made as sturdy, as efficient, and as maintainable as
possible.