Last fall I taught a fourteen week course that covered many practical aspects of designing and implementing object-oriented software. The course focused on developing reusable components using design patterns, frameworks, and C++. The goal of the course was to impart good software development practices through extensive case studies and programming exercises.
The final exams and final programming projects are done. In between battling snowstorms in St. Louis, I've been reflecting on the lessons I learned while teaching this class, and relating them to other teaching experiences I've had during the past eight years. My current teaching responsibilities at Washington University center primarily at the graduate and undergraduate level. I've also taught scores of courses and tutorials on object-oriented design and programming with C++ for professional software developers. In this month's editors corner I'll cover several important lessons I've learned while teaching C++.
Many '80s vintage C++ libraries and frameworks (such as NIHCL and
ET++) were influenced by Smalltalk and other inheritance-based models
for designing object-oriented components. The advent of parameterized
types and STL has changed this model considerably. Thus, '90s vintage
C++ class libraries and frameworks (such as ObjectSpace's
System
Exception handling is another feature that significantly affects C++
program development. Many '80s vintage C++ programmers manage dynamic
objects referenced by pointers allocated on the run-time stack.
Exception handling renders this style of programming obsolete if
robust resource reclamation is needed when errors occur. Thus, '90s
vintage C++ programmers must learn successful strategies and tactics
for writing exception-safe programs. I suspect that standard C++
library "auto_ptr" class will be used frequently in '90s vintage C++
programs.
I've programmed extensively with C++ as it has evolved over the years.
Therefore, absorbing these new features and components has been
incremental and relatively painless. It's tempting, however, to
forget how imposing C++ can appear initially. Teaching C++ to
junior-level undergrads forced me to wrestle with how to introduce and
motivate the C++ feature set concisely and effectively.
During the past eight years I've tried many different ways of teaching
C++. I originally focused on teaching C++ language features.
However, I found that this approach encouraged the use of obscure and
error-prone C++ language features (like user-defined conversions and
class-specific overloaded operator new). In general,
"feature-centric" programming styles yield unwieldy systems that are
overly subtle, non-portable across C++ compilers, and hard to
maintain.
More recently, I've focused on teaching C++ by stressing design
patterns and language idioms. In this approach, I emphasize how
design forces shape software architectures and influence the choice of
certain C++ language features. For example, requirements for
release-to-release binary compatibility discourage the use of inline
functions and macros. Likewise, requirements for run-time efficiency
encourage the use of templates vs. dynamic binding.
I've found this pattern/idiom-centric approach to teaching C++ quite
effective. In particular, students focus on more on the strategic
aspects of their designs, which generally yields more extensible,
decoupled, and maintainable programs. In addition, by focusing on
patterns and idioms FIRST, students are motivated to use key C++
features (like classes, templates, inheritance, and dynamic binding),
rather than the more esoteric features.
It's no surprise that newcomers to C++ often struggle initially with
features like pointers, dynamic memory, and the absence of run-time
bounds checking on arrays. With proper guidance, however, most
students of C++ master the basic language mechanics and evolve into
proficient programmers. Many good books, magazines, and online
resources are available to serve as role models for developers who
aspire to become expert C++ programmers. Good role models are
important -- they help developers internalize idiomatic programming
strategies and tactics that were traditionally learned only through
painful trial and error.
Throughout my teaching career, however, I've found that many
developers have a much harder time evolving into proficient
object-oriented designers. Good designers often exhibit an intuitive,
aesthetic quality in their work. Capturing and conveying this quality
is hard using traditional methods of education. Moreover, until
recently it's been hard to find good resources to serve as role models
for developers who aspire to become expert object-oriented designers.
A key contribution of patterns to the software industry is its focus
on capturing and articulating the "ageless" wisdom of good design and
programming practices.
The dearth of expert software designers has become a serious problem
in domains ranging from telecommunications, health care delivery
systems, financial services, and transaction processing. Due to
increasing pressure to reduce business costs, enhance application
functionality, and leverage hardware advances, software systems have
become more complex and more safety/mission critical. From my
experience developing large-scale commercial telecommunication and
distributed medical imaging systems, projects that lack coherent,
decoupled software designs almost certainly go awry.
To succeed in an increasingly competitive software marketplace, it's
imperative that software professionals develop and hone their design
and programming skills. To be most effective, this learning process
must start early and continue throughout a developer's career.
Unfortunately, many undergraduates in computer science receive
inadequate education and mentoring in good software design and
programming practices. Instead, most university students receive
extensive training in theoretical models (like Turing machines),
formal methods (such as axiomatic language semantics), and algorithmic
analysis techniques (such as solving recurrences). While these topics
are an important foundation, it's increasingly imperative to broaden
the scope of computer science education.
In many universities, there's a conspicuous absence of systematic
coverage on key topics such as software architecture, design patterns,
distributed and concurrent program development, object-oriented
design, and system evolution strategies. As software continues to
become more complex it is increasing necessary to inculcate these
topics into our undergraduate and graduate curriculum. If we want to
make serious progress towards alleviating the "software crisis," we
need to get serious about teaching these topics rigorously from the
start.
At Washington University, we've begun restructuring our curriculum to
address these concerns. If you're interested in seeing what we're
doing, the lecture notes and programming exercises for my course on
object-oriented software design with patterns, framework, and C++ are
freely available online at the following WWW URL: http://www.cs.wm.edu/~schmidt/cs242/
I hope that these articles, along with our other excellent columns and
articles, serve as good role models for your future growth as C++
developers!
Back to
C++ Report Editorials home page.
Lesson 2. Stress good software design and programming concepts
more than C++ features.
One of C++'s greatest strengths is its expressiveness and breadth.
Features like functions, classes, inheritance, virtual functions, and
templates span multiple programming paradigms. While this makes C++
applicable in domains ranging from embedded systems to global
communication systems, newcomers can easily be overwhelmed by the
sheer scope of the language feature set.
Lesson 3. Provide good role models for developing quality software
One of the most poinient lessons I've learned from teaching C++ is
that we need better role models for developers who aspire to become
expert software designers and programmers. In particular, many of my
students over the years had little prior exposure too systematically
scrutinizing good software programming and design practices. I
believe my experiences teaching C++ are a "micro" reflection of a
"macro" problem faced throughout the software industry.
Lesson 4. Successful developers must go beyond object-oriented design
and C++
Becoming an successful developer in the '90s requires much more than
knowledge of object-oriented design techniques and C++ programming
language features. Systems are increasingly built by interconnecting
powerful computers with high-speed networks. Therefore, it's crucial
to understand concepts like concurrency control, persistence,
distribution, and resource sharing. This month's C++ Report features
articles by Sean Leary on a framework for multi-threading and Marina
Kamele on managing persistent shared memory objects. Steve Vinoski
and I examine concurrency models for programming distributed object
servers using C++ and CORBA. In addition, Tom Cargill takes a look at
Java from a C++ programmer's perspective. The surge of interest in
Java is driven largely by its role as the premiere programming
language for World Wide Web applets.