Design issues in computer science education

classes should emphasize uniformity and parsimony by encapsulating information that needs to be hidden and by providing information that needs to be communicated. The decision about what needs to be hidden or communicated must be made in the context of the particular software that is being designed. Hiding hedges a designer's bets about what implementation details are likely to change. As [2] points out, it is impossible to have infinite design flexibility and allow for all possible changes. Therefore, abstract classes need to be tuned to a particular programming situation and the design areas most likely to undergo change. This insight has important pedagogical implications. For one thing, it explains why if you examine a dozen textbooks on data structures you will find a dozen variants of an abstract list class. Each author has envisioned the use of the list class within the context of the software in the book and chosen a set of abstractions to communicate and a collection of details to hide. Since each book has a different teaching philosophy, it is no wonder that these list abstractions turn out to be different. As teachers, we should not attempt to present the list class but should rather discuss the variety of ways in which a list might be abstracted to meet specific design flexibility goals. More generally, abstract classes are not the basic unit of reusable software. What is reusable are concrete classes that are complete and relatively open to access and extension. Pedagogically, this means that we must give careful consideration to the design of concrete classes as well as to the building of abstract layers on top of such classes. The implications of the need to tune abstract classes to the design flexibility goals of a particular piece of software is that abstract classes had better be easy to build. To accomplish this, the member functions in an abstract class must have simple implementations. Ideally, each member function in an abstract class should be implemented by at most three lines of code that refers to some underlying concrete object: setup, make-concrete-call, cleanup. Degrees of Abstraction and Practical Issues Of course, in a practical world (both in industry and in the classroom), we do not always do what is ideal. There is a great temptation to design an abstract interface and then I December 1997 5 Vol. 29 No. 4 inroads SIGCSE Bulletin code substantial algorithmic details into the implementation. However, to the degree that an abstract class does concrete algorithmic work, it ceases to be abstract. If an abstract class does concrete algorithmic work then it will not be easy to change these implementation details and the benefits of abstraction will be lost. At the same time, the algorithmic work will be hidden by an interface which fails to deliver its full potential to the caller. Hence, when the algorithm is needed again in a different context, the class wrapper may have to be redesigned anyway. Let's name this phenomenon. We will call a class quasi-abstract if it has an abstract interface but has an implementation which directly codes substantial algorithmic details. Many examples used in texts for abstract classes are really more precisely quasi-abstract classes. This means that the data representation is hidden properly, but that the algorithmic work has been done inline rather than being similarly encapsulated. To some degree, such a class is a design compromise and should be recognized as such in a teaching setting. Recognition of this compromise is crucial to dealing with it from a design perspective. As educators, the reason that we tend to drift into quasi-abstract classes is twofold. On one hand, we do not have a robust enough set of concrete classes to build upon. On the other hand, to create a truly abstract class, we may need to add another level of abstraction and the pain of additional names and class definitions is too much to bear. Certainly, in the context of busy courses, there is sometimes not enough time to do everything in the best possible way. Nevertheless, we should try to set design standards and, when we need to compromise, we should be explicit about what we are doing and why. Teaching is about being aware. Toolkits and Environments If we wish to teach design then we must work in an environment that is rich in tools. As Eric Roberts states [10]: Students need exposure to modem software development practices. It is no longer acceptable to teach programming by having students write small, self-contained programs from scratch. Even though we cannot predict exactly what toolkits and programming environments students will use when they graduate, it is essential that they learn to work with some modem programming environment that forces them to add small amounts of special-purpose code on top of a large existing infrastructure. Moreover, this experience should come early in the curriculum so that students can apply these techniques in summer internships and their more advanced courses. In addition to reflecting current practice, there are important pedagogical reasons for students to work within a framework of existing code, tools, and class structures. The most important reason for asking students to extend or modify existing code is to permit them to see and explore a large base of well designed code. If we provide a partial solution to an assignment, we have the opportunity to model the kind of software design principles we preach. If we provide a set of tools to be used in a number of assignments, we have a chance to show what we mean by quality reusable software. The code we deliver to students is our stake in the ground about what quality design means in practice. As students progress in the curriculum, they must be exposed to large scale commercial development environments. Although source code is usually not available in such environments, students can still learn by studying the pros and cons of the class libraries provided in the environment. A large scale class library may be used as a set of examples against which the design methodologies in textbooks can be examined. Students can learn about the strengths and weaknesses of both the libraries and the methodologies. It must be recognized that it will not be easy to have faculty everywhere use framework code, toolkits, and advanced environments. Few faculty have the support structure necessary to build large quantities of code. Those of us who have designed and built such pedagogical code must make every effort to distribute this work to faculty at other institutions. However, due to portability issues, this distribution task is not easy. Therefore, it will take time for good code and lab depositories to be put into place. The main obstacle to the faculty use of large scale commercial development environments is the time it takes to grasp the class libraries. With documents that are several inches thick, it must be considered a non-trivial task for faculty to learn an environment well enough to use it in teaching. Hopefully, CS units will begin to allow time for their faculty to pursue this learning process. Design and the Software Project Course In most institutions, students take a junior/senior project course in which they learn about software design and development in a team setting. Ideally, this course should provide a situation in which the design principles learned in earlier courses are put into practice and new design principles applicable to large scale projects are taught. Unfortunately, it is often the case that the project course is a negative design experience. The course is often treated as a rite-of-passage in which students are expected to work ridiculously long hours in order to finish a project on time. SIGCSE Bulletin i n r o a d s Vol. 29 No. 4 6 December 1997 Under these circumstances, there is no incentive for students to ponder a design, explore alternatives, and backtrack to make improvements. Just as in the freshman courses, solving the problem is enough however ugly the code or the design. Of course, students know that there is some penalty for poor design but they also know that the biggest penalty is for not having a running program that roughly meets the specs. We believe that the project course must cease to be a rite-of-passage and instead must become a serious investigation of design issues. Using an incremental approach to building software, students should be encouraged to add class structures and then build working partial solutions every 2-3 days ifpossible. If students "fall behind" in terms of meeting the most ambitious project goals, they should be permitted to "make up the grade" by explaining in writing what design pathways they explored and why their design is of higher quality because of this exploration. The grades will then depend not only on how much the students did but also on what they learned along the way. Design is ultimately a human process and, beyond a reasonable level, stress is counter-productive for the creative work that design requires. If we believe that it is possible to have both rapid development and well designed software [3], then we must use the project course to teach students how this can be accomplished. We must be models of best practice and we must not encourage the mistakes so often committed in industry. We need to make the project course a calm, humane, creative, and rational design experience. Our students will then be able to bring to their future work both good methodologies and good attitudes.