Sponsored Link •
Almost everyone who teaches object orientation uses the class as a fundamental building block. Such an approach misses the central point of object orientation: the objects themselves, and what they portend for flexibility and effective design. This weblog is a case study in teaching object orientation.
Welcome to my new blog! It's a pleasure to be here among such good company. This is my first blogging foray, and I'm looking forward both to exploring this new medium and to using the medium for exploring my own learnings together with the rest of you.
It may be good to start with introductions. I am an Electrical Engineer and Computer Scientist by training, having received degrees in these fields in 1977 and 1979, respectively. I capped off my education with a Ph.D. three years ago. Most of my work career has been at Bell Laboratories, where I started in 1979. I spent my early Bell Labs years on a large development project and then moved on to a technology transfer organization, where I spent more than a decade doing early work with the C++ language, with distributed workstations, and version and configuration management. When I left Bell Labs in 2001, I had been in the Research area for about 10 years. Most of my research focused on software architecture (including the foundational work on software patterns) and organizational structure.
I am a long-time critic of some of the value systems and practices of academics in general and of academic computer science in particular. I have worked over the past decade to gain first-hand credibility with academic culture, both to further my understanding of the issues and to gain the credentials that might one day allow me to change the field from the inside. That's where I am now, and that's what I do. I teach computer science at North Central College, and also teach discrete mathematics and English composition. I am also the Vloebergh Professor of Computer Science at Vrije Universiteit Brussel and am a Visiting Professor of Computer Science at University of Manchester (formerly UMIST).
I plan on using this space to reflect on experiences I haven't yet committed to writing, and particularly my recent experiences as an educator. I frequently admonish my English composition students with Forster's saw: "How can I know what I think until I see what I say?" I hope that mindset serves me well here, and that it provides you an opportunity to challenge your worldviews and to respond in your own pen with your own thoughts.
At the OOPSL A 1999 Educators' Symposium, I emphasized the importance of teaching design in undergraduate computer science curricula. I'm trying to put my money where my mouth is now that I'm in that position. I taught object-oriented analysis and design this term. It became an interesting opportunity because it forced me to think about what's really important about objects: one can leave only so many ideas in a student's head over the course of a few short weeks, so it's important to make a lasting impression about the key elements.
It would have been easy enough to focus on the standard set of principles: coupling and cohesion, perhaps modularity, and of course the piece de resistance, polymorphism. But I found it difficult to justify these principles from any value proposition that I felt I could sell to the students. So it was time to go back to basics, and that's what I did.
And of course I wanted to feature the main attractions of contemporary object-oriented analysis and design. The CS department is driven to a large degree by input from an industry board. That board conveys pressing industrial needs, and we respond. We add formal foundation courses to round out the collection of courses. So in the interest of giving students something practical and current, I knew that both Use Cases and CRC cards would figure strongly in the material.
What I ended up doing was to present design as a two-pronged excercise: structural design, and functional design. Structural design gives a system the long-term stable structure that can reduce discovery costs, localize maintenance work, and provide a foundation for reuse. The main component of structure--in the conventional view--is the system classes and the relationships between them.
The other perspective is functional design. Though it featured more strongly in some of the earliest design approaches for computer programming, it has made a resurgence in object-oriented days in the guise of Use Cases.
Both structure and function are important to profitability. When we think of profitability we most often think of the functional side: What can we sell, and for how much? The functional side is directly visible to the customer. Function is what customers are willing to spend money for; it is the direct customer value that software enterprises deliver. A common view is that good function, delivered in a timely fashion, is the hearbeat of a thriving enterprise.
Profitability isn't only about revenue, but about cost as well. Good structure holds down development costs by localizing change. It might also reduce cost by making the code easier to understand and hence to modify. Even though customers don't care about internal structure--they probably don't even see it--they will pay a price for poor structure in feature delivery schedules and perhaps even in the very feasibility of some features. Experience tends to bear out that poor design shows through all the way to the end user, too, as Brenda Laurel points out in her seminal book Computers as Theatre (Addison-Wesley, 1993). These are competitive considerations in the marketplace, considerations that affect the revenue stream. Beneath these factors lurks the increase in raw development cost that owes to poor structuring: long discovery times, or more intense coordination across major interfaces.
These last few paragraphs seem to have sidetracked us from a focus on good object-oriented pedagogy to business practices. I bring up the business perspective not so much because I feel that academia should take industry as its primary client. Computer science to the contrary not withstanding, academia has a long legacy of being the cradle of great new ideas whose relevance isn't appreciated until many years after their "discovery." However, industrial practice is often a good litmus test for the viability of our design models and of the teaching models that convey them.
I highlight this dichotomy between structure and function because it relates directly to how software design is often taught, as well as because it can relate to the "real concerns" of the business world. We have seen the history of software development pass through fads that cycle back and forth between structure and function. Structured design is often caricatured as relating to structure. It was followed by structured programming, which focused more on program flow (function) than on the structure per se; structure was subordinate to function. Then came abstract data types, and slightly later, object orientation: the functional view got the short shrift. Finally, came Use Cases: a noble attempt to resurrect the functional view. That's where we are now.
I believe there are some simple ways to integrate the functional and structural view. This integration has some surprising results for how we view object oriented design and its activities, and has repercussions that go far beyond pedagogy. Let's have a look at the common contemporary processes of object-oriented design and contrast them with a simple alternative view.
If you look at most syllabi to seek out the single most fundamental concept of object-oriented programming, I'm guessing that we'd find it to be the class.
If you ask people about object-oriented design notations such as UML, most people naturally think about class diagrams. Object diagrams and dynamics are almost always secondary to the class model. Even when faced with an object model of their own making, most programmers have difficulty answering the question: "Where did that object come from, and where was it created?" We extrapolate our static source code view into how we model the application as a whole. Maybe we do that to avoid the complexities of the highly dynamic object structure; an object model either can capture the relationships as they exist at a single moment in time or, as is more likely done, combine some relatively arbitrary collection of relationships that cover a span of execution. But class models already capture many such relationships anyhow.
If you ask people about the building blocks of object-oriented programming they will also likely mention classes. Most popular programming languages use classes as their basic building block. Classes are the foundation of object-based and object-oriented programming in C++. They are unavoidable in Java and Smalltalk. Thinking ahead to coding, most designers bring the programming constructs into their design thinking. This is a natural and healthy practice--but as all practices must be tempered with other considerations as we'll see later.
Trygve Reenskaug (author of Working with Objects, Prentice-Hall, 1996) notes that this is pretty funny: we call it object-oriented programming, yet we tend to focus on classes. There are some strong cognitive reasons behind the human tendency to focus on classes. Humans love to classify and organize to help their understanding. Classes are a way of grouping lots of related objects from the perspective of structure. We feel a sense of power in this grouping. What's more, we become a bit drunk on this same power when we discover that we can group related classes together into a hierarchy under a single base class. Classes become king.
This isn't the first time that the class tradition has come into question. The self programming language, pioneered by Dave Ungar and Randy Smith, demonstrated that a real programming language based on objects instead of classes could deliver powerful flexibility and convenience. Both Ungar's vision of flexibility and Trygve's vision of objects would find vindication later in history as components became popular. Components are heralded for their improved flexibility over objects, something I suspect Ungar always had in mind. Trygve Reenskaug, on the other hand, said that components were what he always envisioned object-oriented programming to be from the beginning. Classes have prevented these two visions from flourishing and in fact may make it difficult for fledgling practitioners to appreciate the fundamental benefits of object orientation itself--particularly the benefits of combining the structural and functional views.
Even though we have Use Cases in a very few contemporary course syllabi, classes rule in software engineering education. We tend to keep Use Cases in our back pocket in case someone asks where we capture functional requirements. But they tend to fall out of our back pocket and get lost once we move on to class design, based perhaps on the misplaced belief that the class member functions capture all we need to remember about the functional view. And the classes have a strongly hierarchical nature to their structure.
But there are two problems here: the functional view still is important, and that the world is rarely very hierarchical. We can "make" the world appear hierarchical from a structural view. But it is rarely possible even to reconcile the functional view with this hierarchical structure. And because the functional view is key to what generates your key revenues, that's a problem: you don't sell classes, you sell functionality.
Much of object-oriented pedagogy (and perhaps of pedagogy in general) seems to be based on hierarchy. The high point of many expositions of object orientation comes with the revelation of inheritance hierarchies supported by run-time polymophism. I think this tendency to hierarchy comes from our need to organize--and that's fine--but I think it goes too far in most expositions of object orientation. It often goes so far as to eliminate information to reduce apparent complexity. There is a term for the elimination of information: abstraction.
While we want to organize essential complexity and minimize accidental complexity, it is dangerous to put aside elements of essential complexity. Such elements will be important either in an early offering of the system or will become important as the system evolves. It's best to put the right framework in place for these "details" up front; it may be almost impossible to add them later. Mies van der Rohe said, "God is in the details."
Hierarchy has become an important abstraction tool in many design paradigms. Used effectively, it brings organizational power. Used to shed essential complexity, it is evil. In most object oriented design, most abstraction is evil. It is an excuse to ignore things we don't want to deal with even though we should.
We start to reduce complexity by grouping objects together into classes, which is in accordance with how most popular programming languages work. These programming languages are popular because they are efficient. But then we take an overly casual and slightly dangerous leap from this hierarchy of objects to hierarchies of classes.
Why is this dangerous? There are several ways to describe this danger, but I'll focus on one. When we group objects together the result isn't an object: it's something different, a class. That's a healthy distinction that helps the designer look at a program in two useful ways: the dynamic structure of objects, and the static, source-code structure of classes. But when we combine a hierarchy of related classes under a common construct that groups them together, we too often just use the concept of "class" again. The problem is that it's not a class any more: it's not a single structure that captures the structure of all the objects of all the classes being grouped together. Making this level of abstraction into a class discards the information that the resulting entity stands in for a larger number of classes in the subtending inheritance graph.
This perspective precipitates two interesting insights. The first is that for a class-based OO educator, this level of compression points out a missing concept: the elusive concept that ties related classes together. That missing concept is a role and has long been featured in what were considered to be offbeat design methods like Reenskaug's OORAM. A role characterizes part of the interface of an object, such that the responsibilities characterize some sphere of knowledge or influence. A given object can take on several roles at once. Consider a Circle object as an example: we can ask it to yield its circumference, location, or area; we can ask it to move, rotate or warp: these owe to a role we might call Shape. We may also want Circle to respond to requests to draw itself on some output medium, or to change its color: these owe to a role called Drawable. An image file object might also respond to all the responsibilities of Drawable, though that may be all that images have in common with Shapes.
The second insight is that progress in programming languages has caught up with this reality: roles appear as a first-class programming construct in the guise of Java interfaces.
There are interesting corrollaries to these insights. The first is that roles are more about objects than they are about classes. A class is a total classification of objects. Rather than thinking of a role as a partial interface to some class, it might be better to think of it as a partial classification of a set of objects. To me, that suggests deferring classes until students have mastered the concepts of objects: the grouping of responsibilities, and the colocation of active data and the functions that operate on them.
Second, because they are not total classifications roles break down the tyrrany of hierarchy. That is a better match for the real world. When God created the world, she did not create it as a hierarchy of classes. She may not even have created it, as Kant might insist, from a universe of discrete, identifiable objects. Roles provide the freedom to define a software reality that can align with a myriad of important design perspectives that range from the marketing view to the structural view. A class is, after all, just an implementation of what might be a degenerate kind of role.
Third, because roles are closer to objects, they are closer to the structure of Use Cases than classes are. Objects are run-time animals, and the role interface to an object reflects a set of responsibilities related more by some function than by any structural consideration. Yet as partial classifications, roles can also bridge the gap to the structural world. This is perhaps the most powerful contribution of roles: as a locus for the intersection of structure and function.
My challenge at the beginning of this term was to sort out the whole notion of object-oriented design for cogent presentation to a wide variety of students. I had a hunch that an object-based focus would win out over a class-based approach (in terms of comprehensibility and conceptual integrity). And I followed the haunting voice of Trygve in the back of my head, both with regard to the centrality of objects and with regard to the place of roles in design. The result was wonderfully satisfying.
Very early in the class, perhaps on the first day, I introduced the traditional design dichotomy between function and structure. I put this in the context of design approaches that historically preceded objects in their own fifteen minutes of fame. That objects were not an end-all and be-all would be a recurring theme in the course, though the course itself focused on objects without going into other approaches in any depth.
I then introduced a simple notion of objects as loci of structure and behavior. From the outside, all we see of an object are behaviors through which we can ask it to do things on our behalf. That was a natural lead-in to abstract data types, with a careful emphasis on the "abstract" and "type" parts of the moniker. Note that students faced ADTs quite a bit before they would encounter classes. I avoided class and language issues, but focused on things that are important from the perspective of Conway's Law--the rule that says that software architecture exists mostly for the sake of the structure of the organization that builds it. With those ideas grounded, I could move on to classes, but more as a convention of packaging than as any important unit of intrinsic structure.
At about the same time I introduced roles as being the analysis or design counterpart of what we had been talking about as ADTs. I made the perfunctory concessions to C++ and its convention to use pure abstract base classes as a way to capture the semantics of roles and ADTs, and appealed to the notion of Java interfaces with which they were already familiar.
Next I introduced Use Cases. We introduced fat and thin Use Cases following a conventional exposition, using Alistair Cockburn's Writing Effective Use Cases book (Addison-Wesley, 2000).
Then, and only then, did I introduce CRC cards. CRC cards are the place where the structure of classes comes together with the dynamics of Use Cases. A hands-on CRC exercise provided the students with a vision of how dynamics help shape the structure, and how the structure yields insight into the dynamics. For me, this was the high point of the object modeling part of the course. It represents the central essence of object orientation.
Oh, yes, we went on to do inheritance and polymorphism to round out the picture, and later went on to more advanced concepts including reflection, aspects, and most other buzzwords of the contemporary object-oriented arena. We also covered the perfunctory issues of covariance and contravariance, building largely on the Liskov Subsitutability Principle but more broadly grounding the issue of subtyping in Bertrand Meyer's taxonomy of IS-A inheritance, supported by Design By Contract. And of course we covered patterns and a few more popular topics. But more importantly we came back and reflected on where we had been, and critiqued the various structure techniques much as one finds in the discussion of the piece you're reading here.
CRC cards were created by Ward Cunningham (see his blog at this site) back in the Smalltalk days at Tektronix (or perhaps it was at Wyatt). Smalltalk features classes as its staple building blocks so it was natural to take those concepts into the design arena.
But a programming language class conveys both behavior and structure, while the CRC card approach quickly settled on a responsibility-driven approach. CRC cards are all about what an object can do rather than being about what an object is. In fact, they look more like roles than like classes! Kent Beck, who has worked closely with Ward over the years and who has done much of the work to popularize CRC cards, once remarked in a workshop that CRC cards weren't really about classes but about roles.
In fact, CRC cards are a powerful hybrid representation that brings classes, objects and roles together. When Kent and Ward do CRC card exercises, they tend to think of a card as an object when you are holding it in the air, and as a class when it's on the table. The responsibility focus is a way to keep the design close to the Use Cases: Use Cases come down to collection of responsibilities, and object-oriented design is in large part the activity of grouping those responsibilities together. CRC cards do that. The resulting entities may or may not be building blocks; what makes a good building block depends in part on embedded software, available software packages, and even tradition. CRC cards can capture that, too
This perspective on Use Cases, objects, roles and classes let me integrate them together around a small number of intuitive concepts. If students understand what responsibilities are, they can understand Use Cases and can certainly understand roles. Looking at objects as locales of structure and behavior makes object-oriented design "real" in terms of the programming world familiar to them, and bringing in the concept of classes as optimizations (sharing member function code and a structural skeleton across objects) makes it concrete in terms of a programming language. Rather than clumsily introducing Use Cases as a front-end to the "find the objects" exercise, or of explaining the similarities and differences between responsibility-based design and some other flavor of object-oriented design, this approach serves to point to a larger, single underlying model of which classes, objects, Use Cases and roles are just facets.
It is this underlying model that I would call the object paradigm.
By the end of the course, students could tie together the basic concepts of responsibilities, objects, classes, and roles. They understood how Use Cases fit into the process. They understood the interplay between the functional and structural views of the system as embodied in Use Case responsibilities and classes respectively--a view that takes students all the way from the classes of programming to the organizational structures that separate functional concerns from structural concerns.
I'm guessing that most people who teach CRC cards teach them as classes rather than as roles. People gravitate toward classes for the same reasons we discussed earlier: reducing the amount of information one must deal with, and talking about design in terms that already are familiar from programming. I am concerned that this misses the essential foundations not only of CRC cards but, more worrisomely, the responsibility-based foundations of object orientation itself.
And here's the punch line: it isn't only pedagogy that falls short on these issues. Most designers in the real world tend to work the same way. It is at least sad, and perhaps scary, that most people are using object orientation in a way that is based on modules with polymorphism and instantiation rather than on a more living design approach that embraces Use Cases and the dynamic view that objects bring.
In some sense, one can look at this exercise in crafting a course on object-oriented analysis and design as an exercise in building an object model. It's important to understand that I didn't approach it that way. I focused on fundamentals, fundamentals that led to fundamental benefits such as ease of evolution, accommodation for Conway's Law, software compression, and relationship to business goals. It was a quite un-academic tact. A more academic tact would have started either with programming language or perhaps wth type theory and the semantics of inheritance.
It was satisfying to see that this approach tied to the fundamental notions of design in a way that may not be satisfying to someone looking to tout the benefits of a given programming language or methodology, but that might be more satisfying to someone with a degree in engineering or architecture. Design is the collection of activities and tools that take one from the understanding of a problem to its solution.
What problem you are solving? In a companion course on Systems Analysis, we teach students that this is one of the most important questions you can ask. This question is too often lost in most contemporary object-oriented development, since objects focus more on the structural component of design than on the functional leg of design.
There was an activity in the 1970s called the "design movement" that included people as disparate as Peter Naur (the "N" in "BNF") and Christopher Alexander. (See Developments in Design Methodology by Nigel Cross, Wiley, 1984.) Much of their work focused on the interplay back and forth between the problem domain and solution domain. The "problem domain" brings customer desires and needs. What the design movement learned is that this customer view should be tempered with the realities that unfold during design. In software, we know this as iterative design. In the object-oriented analysis course we typified the solution component as the class structure, and the customer problem component as the Use Cases. CRC cards are the place where these two worlds come together--where struggles with the solutuion offer insight into the problem itself. Reconciling these forces both keeps the customer happy by meeting functional requirements, and keeps the enterprise happy by leading to a structure that best accommodates evolution.
Infusing roles is a good lead-in for many other features of the object community. Aspects can be described as collections of roles in a more straightforward way than is feasible from a basis in classes. The focus on Conway's Law is a direct lead-in to refactoring.
These results are not just academic. They do in fact lead to an object model and a design approach that accommodates that model. But it is more than an object model: it is also a business model and a process model, a model that is practical, teachable, and perhaps even powerful. What, in summary, is the development approach based on that model?
That's it. On one hand, none of the notions are individually profound. But on the other hand, this perspective combines a small number of fundamental concepts into a consistent, integrated picture of the fundamentals of object orientation.
|Jim Coplien is a Senior Agile Coach and System Architect at Nordija A/S, doing international consulting in organization structure, software patterns, system architecture, as well as software development in electronic design automation, telecom, and finance. In this 'blog, he reflects on his academic career pursuant to his visiting professorship at University of Manchester Institute of Science and Technology, his appointment as the 2003-2004 Vloebergh Chair at Vrije Universiteit Brussel, two years as an Associate Professor at North Central College in Naperville, Illinois, and extensive work developing some of the first C++ and OOD training materials. He is well-known for his foundational work on object-oriented programming and on patterns, and his current research explores the formal foundations of the theory of design, foundations of aesthetics and beauty, and group theoretic models of design structure. He most recent book "Organizational Patterns of Agile Software Development", co-authored with Neil Harrison, culminates a decade of research. His book "Advanced C++ Programming Styles and Idioms" defined design and programming techniques for a generation of C++ programmers, and his book "Multi-Paradigm Design for C++" presents a vision for the evolution of current OO design techniques into a more general and better grounded theory of software construction.|