📄 Page
1
M A N N I N G Marco Faella Foreword by Cay Horstmann Code that works, survives, and wins
📄 Page
2
The programmer’s journey This book is intended as a guide to the middle part of the trip.
📄 Page
3
Seriously Good Software Code that Works, Survives, and Wins MARCO FAELLA Foreword by Cay Horstmann MANN ING Shelter Island
📄 Page
4
For online information and ordering of this and other Manning books, please visit www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact Special Sales Department Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 Email: orders@manning.com ©2020 by Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used bymanufacturers and sellers to distinguish their products are claimed as trade- marks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. ∞ Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine. Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 Development editor: Toni Arritola Technical development editor: Kostas Passadis Production editor: Janet Vail Copy editor: Carl Quesnel Proofreader: Keri Hales Technical proofreader: Serge Simon Typesetter: Paolo Brasolin Cover designer: Marija Tudor ISBN 9781617296291 Printed in the United States of America
📄 Page
5
brief contents Part 1 Preliminaries . ......................................................................1 1 Software qualities and a problem to solve 3 2 Reference implementation 25 Part 2 Software Qualities . ........................................................ 45 3 Need for speed: Time efficiency 47 4 Precious memory: Space efficiency 81 5 Self-conscious code: Reliability throughmonitoring 117 6 Lie to me: Reliability through testing 153 7 Coding aloud: Readability 187 8 Many cooks in the kitchen: Thread safety 217 9 Please recycle: Reusability 247 iii
📄 Page
6
(This page has no text content)
📄 Page
7
contents foreword xi preface xiii acknowledgments xv about this book xvii about the author xxi about the cover xxii Part 1 Preliminaries ...................................................................... 1 1 Software qualities and a problem to solve 3 1.1 Software qualities 4 Internal vs. external qualities 4 Functional vs. nonfunctional qualities 5 1.2 Mostly external software qualities 5 Correctness 6 Robustness 7 Usability 7 Efficiency 7 1.3 Mostly internal software qualities 8 Readability 8 Reusability 8 Testability 9 Maintainability 9 1.4 Interactions between software qualities 10 1.5 Special qualities 11 Thread safety 11 Succinctness 12 v
📄 Page
8
vi CONTENTS 1.6 The recurring example: A system of water containers 12 The API 12 The use case 14 1.7 Data model and representations 15 Storing water amounts 16 Storing connections 17 1.8 Hello containers! 19 Fields and constructor 19 Methods getAmount and addWater 20 Method connectTo 21 2 Reference implementation 25 2.1 The code 27 Memory layout diagrams 28 The methods 31 2.2 Memory requirements 32 Memory requirements of Reference 34 2.3 Time complexity 36 Time complexity of Reference 39 2.4 Applying what you learned 39 Part 2 Software Qualities . ........................................................ 45 3 Need for speed: Time efficiency 47 3.1 Adding water in constant time 49 Time complexity 51 3.2 Adding connections in constant time 51 Representing groups as circular lists 51 Delaying the updates 54 3.3 The best balance: Union-find algorithms 57 Finding the group representative 58 Connecting trees of containers 61 Worst-case time complexity 63 Amortized time complexity 64 Amortized analysis of resizable arrays 66 3.4 Comparing implementations 69 Experiments 69 Theory vs. practice 70 3.5 Andnow for something completely different 72
📄 Page
9
vii Fast insertion 73 Fast queries 73 Fast everything 74 3.6 Real-world use cases 74 3.7 Applying what you learned 75 4 Precious memory: Space efficiency 81 4.1 Gently squeezing 82 Space and time complexity 84 4.2 Plain arrays 87 Space and time complexity 88 4.3 Forgoing objects 90 The object-less API 91 Fields and the getAmount method 93 Creating containers with a factory method 94 Connecting containers by ID 97 Space and time complexity 100 4.4 The black hole 100 Space and time complexity 103 4.5 Space-time trade-offs 104 4.6 Andnow for something completely different 106 Low duplicate count 106 High duplicate count 107 4.7 Real-world use cases 108 4.8 Applying what you learned 109 5 Self-conscious code: Reliability through monitoring 117 5.1 Design by contract 118 Pre- and postconditions 118 Invariants 120 Correctness and robustness 121 Checking contracts 122 The broader picture 124 5.2 Designing containers by contract 126 5.3 Containers that check their contracts 128 Checking the contract of addWater 128 Checking the contract of connectTo 132 5.4 Containers that check their invariants 135 Checking the invariants in connectTo 136 Checking the invariants in addWater 138
📄 Page
10
viii CONTENTS 5.5 Andnow for something completely different 139 The contracts 139 A baseline implementation 140 Checking the contracts 141 Checking the invariants 142 5.6 Real-world use cases 143 5.7 Applying what you learned 144 6 Lie to me: Reliability through testing 153 6.1 Basic testing notions 154 Coverage in testing 154 Testing and design-by- contract 155 JUnit 156 6.2 Testing containers 158 Initializing the tests 158 Testing addWater 160 Testing connectTo 166 Running the tests 168 Measuring code coverage 168 6.3 Testability 170 Controllability 170 Observability 170 Isolation: Severing dependencies 171 6.4 Andnow for something completely different 173 Improving testability 174 A test suite 176 6.5 Real-world use cases 178 6.6 Applying what you learned 179 7 Coding aloud: Readability 187 7.1 Points of view on readability 188 Corporate coding style guides 189 Readability ingredients 190 7.2 Structural readability features 190 Control flow statements 191 Expressions and local variables 193 7.3 Exterior readability features 194 Comments 194 Naming things 195 White space and indentation 196 7.4 Readable containers 197
📄 Page
11
ix Documenting the class header with Javadoc 197 Cleaning connectTo 201 Cleaning addWater 205 7.5 Final thoughts on readability 206 7.6 Andnow for something completely different 207 7.7 Real-world use cases 209 7.8 Applying what you learned 210 8 Many cooks in the kitchen: Thread safety 217 8.1 Challenges to thread safety 218 Levels of concurrency 220 A concurrency policy for water containers 222 8.2 Dealing with deadlocks 223 Atomic lock sequences 224 Ordered lock sequences 225 A hidden race condition 227 8.3 Thread-safe containers 228 Synchronizing connectTo 228 Synchronizing addWater and getAmount 230 8.4 Immutability 232 The API 233 The implementation 235 8.5 Andnow for something completely different 238 8.6 Real-world use cases 239 8.7 Applying what you learned 240 9 Please recycle: Reusability 247 9.1 Establishing boundaries 247 9.2 The general framework 249 The attribute API 252 Mutable collectors 253 Adapting Attribute to functional interfaces 258 9.3 A generic container implementation 259 9.4 General considerations 260 9.5 Recovering water containers 262
📄 Page
12
x CONTENTS Updated use case 262 Designing the concrete attribute 262 Defining the concrete water container class 264 9.6 Social network posts 265 9.7 Andnow for something completely different 266 An interface for parametric functions 268 A communication discipline 270 9.8 Real-world use cases 271 9.9 Applying what you learned 273 appendix A Code golf: Succinctness 281 appendix B The ultimate water container class 285 index 289
📄 Page
13
foreword I have written quite a few programming books in the last 30 years, so it is perhaps not surprising that every so often someone contacts me seeking advice about writing a book. I always ask for a sample chapter. In most cases, I hear nothing further, and I don’t feel bad about that. Obviously, if someone can’t produce a chapter, no book will materialize, and there is nothing to discuss. In January 2018, I got an email from Marco Faella, a professor from the University of Naples, whom I had previously met when he worked at the University of California in Santa Cruz. He asked for advice about a book project. And he had already finished several chapters! I liked what I saw and replied with encouragement and a few sugges- tions. I didn’t hear anything further. I wasn’t surprised. One of my editors once told me that he knew a great number of people who started a book . . . but only a few who finished one. In April 2019, I got another email. The book was going to be published by Manning. And it looked really good. In August, Marco asked me to write the foreword, which I am delighted to do. When I write a book about a programming language (such as the classic Core Java book), I focus on the constructs and APIs that are specific to that language. I assume that the reader has a good grasp of data structures, algorithms, and software engineer- ing principles such as testing, refactoring, and design patterns. Of course, having been a professor, I am aware that university curricula do not always do a good job of teaching those topics in a practical and accessible manner. That is where this book fills a real need. You, the reader, are expected to be familiar with the fundamentals of Java programming, andMarco will show you the way to higher quality programs. You probably have experience with algorithm design, API design, testing, and concurrency, but Marco puts a new spin on these classic themes. He takes xi
📄 Page
14
xii FOREWORD one example and derives an astounding number of insights from it by implementing it over and over in different ways. Normally, I dislike the “running example” technique because it forces me to read a book sequentially. I can’t just dive in at a spot that interests me because I need to know the evolutionary state of the running example. But Marco’s example (whose nature I don’t want to reveal in the foreword) is very cleverly designed. You need to master a couple of surprising—and interesting—core concepts when you first see it. Afterwards, each chapter evolves the code in an independent direction. It is quite a tour de force. In each of the principal chapters, you will find a section titled “And now for some- thing completely different.” At this point, you are invited to apply the chapter’s tech- niques to a different situation. I encourage you to work through these challenges, as well as the pop quizzes and end-of-chapter exercises. Building high-quality software is never simple, and it is always a good idea to re- examine the principles of good design and craftsmanship. In this book, you will find a fresh perspective that I hope you will enjoy as much as I did —Cay Horstmann Author of Core Java, Java/Scala/JavaScript for the Impatient, andmany other books for beginning and professional programmers
📄 Page
15
preface My personal title for this book is “Java: Exercises in Style.” After the wise people at Manning taught me about catchiness, this preface is all that’s left of that title and its lit- erary overtone. Indeed, in the modern classic Exercises in Style, French writer Raymond Queneau writes the same story in 99 different ways. The point of the book is not the story, which is intentionally unremarkable, but rather the whimsical exploration of the virtually endless expressive capabilities of natural languages. Programming is certainly not literature, despite efforts by renowed personalities such as Donald Knuth to bring the two closer together. In fact, beginner program- mers may be forgiven if they think there’s one best way to solve each programming assignment, just like simple math problems have a single solution. In reality, modern day programming can be much more similar to literature than to math. Programming languages have evolved to include ever more abstract constructions, multiplying the ways to achieve the same goal. Even after their introduction, languages evolve, often by acquiring new ways of doing the same things. The most used languages, such as Java, have been evolving at an accelerated pace to keep up with the younger languages trying to take their positions. In this book, I try to give a taste of the wide range of concerns and solutions that you should consider, or at least be aware of, when undertaking any programming task. The task I propose is quite mundane: a class representing water containers that you can connect with pipes and fill with liquid. Clients interact continuously with the con- tainers, adding or removing water and placing new pipes at any time. I present and discuss 18 different implementations for this task, each striving to maximize a different objective, be it performance, code clarity, or some other software quality. This book is not a dry sequence of code snippets. Whenever the context calls for it, I take the opportunity to discuss a number of specialized topics pertaining to computer science xiii
📄 Page
16
xiv PREFACE (various data structures, complexity theory, and amortized complexity); Java program- ming (thread synchronization and the Java memory model); and software engineering (the design-by-contract methodology and testing techniques). My objective is to show you that even a relatively simple case study, when analyzed in depth, is linked to a vast network of topics, all of which contribute to writing better code. FURTHER READING One of the objectives of this book is to stimulate your curiosity about the various dis- ciplines related to software development. That’s why each chapter ends with a Fur- ther reading section, where I briefly introduce the best resources I could find on the chapter’s topic. I think the preface should be no exception, so here it goes: Raymond Queneau. Exercises in Style. New Directions, 2013. The original “exercises in style” book. (Actually, the original was written in French in 1947.) Cristina Videira Lopes. Exercises in Programming Style. CRC, 2014. The author solves a simple programming task in 33 different styles using Python. Rather than optimizing different code qualities, every style results from obey- ing certain constraints. Among other things, it teaches you a lot about the history of programming languages. Matt Madden. 99 Ways to Tell a Story: Exercises in Style. Jonathan Cape, 2006. When you need a break from coding, check out this comic book featuring a simple story drawn in 99 different styles.
📄 Page
17
acknowledgments I expected to work hard on this book. I didn’t expect that so many other people would work on it equally hard. I’m talking about the people at Manning, who in various capacities pushed the quality of this book close to my personal limit. A number of external reviewers took time to write detailed reports that helped refine the content. I hope they’re happy with the result. I’ve learned Java from Cay’s books, and I’ve been recommending them to my stu- dents for many years. I’m honored that he kindly agreed to write the foreword for this book. To all the reviewers: Aditya Kaushik, Alexandros Koufoudakis, Bonnie Bailey, Elio Salvatore, Flavio Diez, Foster Haines, Gregory Reshetniak, Hessam Shafiei Moqaddam, Irfan Ullah, Jacob Romero, John Guthrie, Juan J. Durillo, Kimberly Winston-Jackson, Michał Ambroziewicz, Serge Simon, Steven Parr, Thorsten Weber, and Travis Nelso, your suggestions helped make this a better book. The origin of my passion for programming can be traced back to my father teaching a curious eight-year-old me how to draw a circle with a for loop and two enchantments called cos and sin. Grazie! xv
📄 Page
18
(This page has no text content)
📄 Page
19
about this book The core idea of this book is to convey the mindset of an experienced developer by comparing and contrasting different code qualities (aka nonfunctional requirements). The figure inside the front cover relates the content of this book to the wider land- scape of the knowledge required of professional developers. First, you start by learn- ing the basics of a programming language. In Java, that entails knowing about classes, methods, fields, and so on. This book won’t teach you those basics. Then, you should ideally follow three paths: The programming language path—Learn about the more advanced language fea- tures, such as generics and multithreading. The algorithmic path—Learn about solid theoretical principles, standard algo- rithms and data structures, and ways to measure their performance. The software engineering path—Learn about design principles, methodologies, and best practices that help manage complexity, especially in large projects. This book covers all of these areas, with a twist. Rather than teaching these different aspects separately, I’ll mix and match them according to the needs of each chapter. Each chapter focuses on a specific software quality, like time efficiency or readability. I’ve selected the chosen qualities not only because of their importance and universality, but also as those that you can meaningfully apply to a small code unit (a single class). Moreover, I try to focus on general principles and coding techniques, rather than spe- cific tools. When appropriate, I’ll point to tools and libraries that can help you assess and optimize a given software quality. xvii
📄 Page
20
xviii ABOUT THIS BOOK WHO SHOULD READ THIS BOOK This book is the ideal starting point for a junior developer with limited formal training to widen their perspective on software development. Specifically, the book targets two objectives and audiences: For working developers with little formal training, or training in a different area than CS/CE, it provides a tour of computer science and engineering techniques, elucidating the trade-offs inherent in any nontrivial programming task. For computer science and engineering students, it provides a unifying case study for a variety of topics that are traditionally taught in different courses. As such, it may supplement your textbooks for programming and software engineering classes. In both cases, to make the most out of this book you should be familiar with the following: Basic programming notions, such as iteration and recursion Basic object-oriented programming notions, such as encapsulation and inher- itance Intermediate Java language skills, including generics, standard collections, and basic multithreading (thread creation and the synchronized keyword) BOOK STRUCTURE Here’s a breakdown of the chapters and their corresponding code qualities. Don’t over- look the hands-on exercises at the end of all chapters. They come with detailed solu- tions and complete the core chapter content by applying those techniques in different contexts. Chapter 1 The first chapter describes the programming task to be solved (a water con- tainer class), followed by a naive implementation showcasing commonmiscon- ceptions that affect inexperienced programmers. Chapter 2 I detail the reference implementation (which I subsequently refer to as Refer- ence), which strikes a balance between different qualities. Chapter 3 Focusing on time efficiency, you’ll improve the running time of Reference by up to two orders of magnitude (500x), and discover that different use cases lead to different performance trade-offs. Chapter 4 This chapters experiments with space (memory) efficiency. Compared to Ref- erence, you’ll shrink the memory footprint of containers by more than 50% when using objects and by 90% when forgoing an object for each water con- tainer. Chapter 5 To achieve reliability via monitoring, I introduce the design by contract method- ology and show you how to harden the Reference class with runtime checks and assertions based on method contracts and class invariants.