Introduction to Software Design with Java [1 ed.] 9783030240936

This textbook provides an in-depth introduction to software design, with a focus on object-oriented design, and using th

2,453 261 6MB

English Pages 303 Year 2019

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Introduction to Software Design with Java [1 ed.]
 9783030240936

Citation preview

Martin P. Robillard

Introduction to Software Design with Java

Introduction to Software Design with Java

Martin P. Robillard

Introduction to Software Design with Java

Martin P. Robillard School of Computer Science McGill University Montreal, Québec, Canada

© 2017 Circlecreativestudio/iStock ISBN 978-3-030-24093-6 ISBN 978-3-030-24094-3 (eBook) https://doi.org/10.1007/978-3-030-24094-3 © Springer Nature Switzerland AG 2019 This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed. The use of general descriptive names, registered names, trademarks, service marks, etc. in this publication does not imply, even in the absence of a specific statement, that such names are exempt from the relevant protective laws and regulations and therefore free for general use. The publisher, the authors, and the editors are safe to assume that the advice and information in this book are believed to be true and accurate at the date of publication. Neither the publisher nor the authors or the editors give a warranty, express or implied, with respect to the material contained herein or for any errors or omissions that may have been made. The publisher remains neutral with regard to jurisdictional claims in published maps and institutional affiliations. This Springer imprint is published by the registered company Springer Nature Switzerland AG The registered company address is: Gewerbestrasse 11, 6330 Cham, Switzerland

Preface

This book is inspired by well over a decade of teaching software design at McGill University. At first, my focus was to explain the software design know-how available from high-quality references. Soon, however, I realized that the main challenge of teaching software design lay elsewhere. Communicating how to apply a design technique or use a programming language mechanism was relatively easy. The real struggle was to convey in which context we want to use a certain design technique, and why. To do this, I needed to explain what is going on in a software developer’s head. Over time, my lectures came to be more about exploring the space of alternative design decisions one can make in a given context. The goal of this book is to help readers learn software design by discovering the experience of the design process. I share my experience of designing software through a narrative that introduces each element of design know-how in context, and explores alternative solutions in that context. The narrative is supported by hundreds of code fragments and design diagrams. My hope is that this book can serve as an effective resource and guide for learning software design. However, I do not believe that it is possible to develop significant design skills solely by reading a book. In my own learning process, I have benefited hugely from reading other people’s code, regularly writing code, and relentlessly refactoring existing code to experiment with alternative design solutions. For this reason, this book places a lot of emphasis on coding and experimentation as a necessary complement to reading the text. To support this aspect of the learning process, I provide a companion website with practice problems, and three example applications that capture numerous design decisions. An orientation through these example applications is provided in a section called “Code Exploration”, at the end of each chapter. As its title indicates, this book provides an introduction to software design using the Java language. The code used throughout the book, as well as the example applications, are in Java (version 8). My use of the Java language, however, is a means to communicate design ideas, and not the topic of the book. I aimed to cover design concepts and techniques that are applicable in a host of technologies. Many concepts (such as encapsulation), will be generally relevant in any technology. Others v

vi

Preface

(such as inheritance) will be paradigm-specific, but usable in multiple programming languages. For both general and paradigm-specific information, it should be possible to adapt the examples to other languages relatively easily. In a few cases, the material needs to address a Java-specific mechanism with implications on design (e.g., cloning). In such cases, I make sure to present the mechanism as one implementation of a more general technique. This book is targeted at readers who have a minimum of programming experience and want to move from writing small programs and scripts to tackling the development of larger systems. This audience naturally includes students in university-level computer science and software engineering programs. However, I kept the prerequisites to specific computing concepts to a minimum, so the content is also accessible to programmers without a primary training in computing. In a similar vein, understanding the code fragments requires only a bare minimum knowledge of the language, such as would be taught in an introductory programming course. Information about Java that is crucial to understand the text is provided in an appendix, more advanced features are introduced and explained as necessary, and I make a minimum of references to specific elements of the language’s class library. My hope is thus that the book can be useful to almost anyone who wants to write clean, well-designed software.

Organization of the Book The first chapter is a general introduction to software design. The subsequent chapters provide a progressive coverage of design concepts and techniques presented as a continuous narrative anchored in specific design problems. In addition to the main content, the book includes different features to orient readers and help use the book as a launchpad for further exploration and learning. • Chapter Overview: At the beginning of each chapter, a callout lists the concepts and principles, programming mechanisms, design techniques, and patterns and antipatterns covered in the chapter. • Design Context: Following the overview, a paragraph titled “Design Context” introduces the design problems that are used as running examples in the chapter. It is thus not necessary to read all previous chapters to understand the code discussed in a given chapter. • Diagrams: Each chapter includes numerous diagrams that illustrate design ideas. Although they are provided to illustrate the ideas in the text, the diagrams are also realistic illustrations of diagrams that can be used in practice as part of design discussions. • Code Fragments: Each chapter includes many code fragments. The code generally follows the conventions presented in Appendix B, with occasional concessions made to make the code more compact. A complete version of the code fragments can be downloaded from the companion website (see below). • Insights: In each chapter, the main numbered sections are followed by an unnumbered section titled “Insights”. This section forms an actionable summary of

Preface



• •



vii

the key information and advice provided in the chapter. It is meant as a sort of catalog of applicable design knowledge, and assumes the material in the chapter has been mostly assimilated. The insights are in bullet points to be easily perused. Code Exploration: Following the “Insights” section is a section titled “Code Exploration” that provides a discussion of software design in practice. To facilitate good flow and avoid getting lost in details, the design contexts discussed in the main chapters are kept as simple as possible. As a result, some interesting aspects of the software design experience do get lost in the simplification. The code exploration activity supported by this section is the opportunity to consider how some of the topics presented in the chapter manifest themselves in reality. The “Code Exploration” section points to specific parts of the code of the example applications. Before reading the text in the Code Exploration section, I recommend reviewing the code referenced and trying to understand it as much as possible. The example applications discussed in the “Code Exploration” section are described in Appendix C. They include JetUML, the application used to create all the diagrams in the book. Further Reading: The Further Reading section provides pointers to references that complement the material presented in the chapter. Companion Website Additional resources for this book are available in the repository https://github.com/prmr/DesignBook. The material in the repository includes a complete and commented version of the code that appears in the chapter content, as well as practice exercises and their solutions. Example Applications The three Java applications described in Appendix C were developed following many of the principles and techniques described in the book, and are provided as an accessible basis for additional study and exploration.

Acknowledgments I am most grateful to Mathieu Nassif, who carried out a detailed technical review of the entire manuscript, providing me with hundreds of corrections, suggestions, and interesting points for discussion. I warmly thank Jin Guo for reviewing most of chapters and testing some of the material in her own teaching, and Kaylee Kutschera, Brigitte Pientka, and Clark Verbrugge for feedback on various parts of the manuscript. I am also thankful to Ralf Gerstner, the executive editor in charge of computer science at Springer, for believing in the project from the start and for seeing it through with his usual diligence and professionalism. Martin P. Robillard April 2019

Contents

1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1 Defining Software Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2 Design in the Software Development Process . . . . . . . . . . . . . . . . . . . 6 1.3 Capturing Design Knowledge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4 Sharing Design Know-How . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2

Encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1 Encapsulation and Information Hiding . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Encoding Abstractions as Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Scopes and Visibility Restrictions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Object Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Escaping References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6 Immutability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7 Exposing Internal Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.8 Design by Contract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13 14 14 18 20 23 26 28 31 34 34 37

3

Types and Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1 Decoupling Behavior from Implementation . . . . . . . . . . . . . . . . . . . . . 3.2 Specifying Behavior with Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Class Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4 Function Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5 Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6 The I TERATOR Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.7 The S TRATEGY Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.8 The Interface Segregation Principle . . . . . . . . . . . . . . . . . . . . . . . . . . . Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39 40 43 45 47 50 53 54 55 58 ix

x

Contents

Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4

Object State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1 The Static and Dynamic Perspectives of a Software System . . . . . . . 4.2 Defining Object State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 State Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4 Designing Object Life Cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 Nullability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6 Final Fields and Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.7 Object Identity, Equality, and Uniqueness . . . . . . . . . . . . . . . . . . . . . . 4.8 The F LYWEIGHT Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.9 The S INGLETON Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.10 Objects of Nested Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

61 61 62 64 66 68 74 76 79 82 83 87 88 89

5

Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 5.1 Introduction to Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 5.2 Unit Testing Framework Fundamentals with JUnit . . . . . . . . . . . . . . . 94 5.3 Organizing Test Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.4 Metaprogramming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 5.5 Structuring Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 5.6 Tests and Exceptional Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 5.7 Encapsulation and Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 5.8 Testing with Stubs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 5.9 Test Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

6

Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 6.1 Composition and Aggregation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 6.2 The C OMPOSITE Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 6.3 Sequence Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 6.4 The D ECORATOR Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 6.5 Combining C OMPOSITE and D ECORATOR . . . . . . . . . . . . . . . . . . . . . 136 6.6 Polymorphic Object Copying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 6.7 The P ROTOTYPE Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 6.8 The C OMMAND Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 6.9 The Law of Demeter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

Contents

xi

7

Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 7.1 The Case for Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 7.2 Inheritance and Typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 7.3 Inheriting Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 7.4 Inheriting Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 7.5 Overloading Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 7.6 Inheritance Versus Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 7.7 Abstract Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.8 Revisiting the D ECORATOR Design Pattern . . . . . . . . . . . . . . . . . . . . . 175 7.9 The T EMPLATE M ETHOD Design Pattern . . . . . . . . . . . . . . . . . . . . . . 177 7.10 Proper Use of Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189

8

Inversion of Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 8.1 Motivating Inversion of Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 8.2 The Model–View–Controller Decomposition . . . . . . . . . . . . . . . . . . . 194 8.3 The O BSERVER Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 8.4 Applying the O BSERVER Design Pattern . . . . . . . . . . . . . . . . . . . . . . . 205 8.5 Introduction to Graphical User Interface Development . . . . . . . . . . . . 208 8.6 Graphical User Interface Component Graphs . . . . . . . . . . . . . . . . . . . . 211 8.7 Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 8.8 The V ISITOR Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236

9

Functional Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 9.1 First-Class Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 9.2 Functional Interfaces, Lambda Expressions, and Method References 239 9.3 Using Functions to Compose Behavior . . . . . . . . . . . . . . . . . . . . . . . . . 246 9.4 Using Functions as Suppliers of Data . . . . . . . . . . . . . . . . . . . . . . . . . . 250 9.5 First-Class Functions and Design Patterns . . . . . . . . . . . . . . . . . . . . . . 257 9.6 Functional-Style Data Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 Insights . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 Code Exploration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272

A

Important Java Programming Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . 273 A.1 Variables and Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 A.2 Objects and Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 A.3 Static Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 A.4 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 A.5 Packages and Importing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277

xii

Contents

A.6 Generic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 A.7 Collection Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 A.8 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 B

Coding Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281

C

Example Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285

References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289

Chapter 1

Introduction

In 1988, a fascinating little piece of code hits the limelight. That year, one of the winners of the annual International Obfuscated C Code Contest features a program that writes out to the terminal console the text of an eighteenth-century poem titled “The Twelve Days of Christmas”. Figure 1.1 shows the first three verses of the text, as they appear on the output console when executing the code. This poem is particular in that its text has a regular structure. Text with such a structure is amenable to being constructed by software in a way that goes beyond printing hard-coded data. With a poem like “The Twelve Days of Christmas”, there was thus opportunity for creating a clear and compact solution for displaying a poem on the console. However, as promised by the name of the contest where it was featured, the program is anything but clear. If fact, its inner workings are unfathomable. Figure 1.2 reproduces the complete code of the program.

On the first day of Christmas my true love gave to me a partridge in a pear tree. On the second day of Christmas my true love gave to me two turtle doves and a partridge in a pear tree. On the third day of Christmas my true love gave to me three French hens, two turtle doves and a partridge in a pear tree. ... Fig. 1.1 Partial output of “The Twelve Days of Christmas” program of Figure 1.2

In a way, this quirky piece of computer science trivia illustrates the impact of a lack of self-evident structure in software. Here we have a programming problem with trivial requirements: the functionality of interest requires no argument and produces a single, unchangeable output. Yet, the code to support this functionality © Springer Nature Switzerland AG 2019 M. P. Robillard, Introduction to Software Design with Java, https://doi.org/10.1007/978-3-030-24094-3_1

1

2

1 Introduction

main(t,_,a ) char* a;{return!0