Previous Next

99 Bottles of OOP A Practical Guide to Object-Oriented Design (JavaScript Edition) (Sandi Metz, Katrina Owen, TJ Stankus) (z-library.sk, 1lib.sk, z-lib.sk)

Author: Sandi Metz, Katrina Owen, TJ Stankus

JavaScript

It turns out that everything you need to know about Object-Oriented Design (OOD) can be learned from the "99 Bottles of Beer" song. Well, perhaps not everything, but quite certainly a great many things. The song is simultaneously easy to understand and full of hidden complexity, which makes it the perfect skeleton upon which to hang lessons in OOD. The lessons embedded within the song are so useful, and so broad, that over the last three years it has become a core part of the curriculum of Sandi Metz’s Practical Object-Oriented Design course. The thoughts in this book reflect countless hours of discussion and collaboration between Sandi, Katrina Owen, and TJ Stankus. These ideas have been battle-tested by hundreds of students, and refined by a series of deeply thoughtful co-instructors, beginning with Katrina. While none of the authors have the hubris to claim perfect understanding, all have learned a great deal about Object-Oriented Design from teaching this song, and feel compelled to write it all down. Therefore, this book, now in its second edition. We hope that you find it both useful and enjoyable.

📄 File Format: PDF
💾 File Size: 2.4 MB
12
Views
0
Downloads
0.00
Total Donations

📄 Text Preview (First 20 pages)

ℹ️

Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

📄 Page 1
(This page has no text content)
📄 Page 2
99 Bottles of OOP Metz, Sandi;Owen, Katrina;Stankus, TJ Table of Contents Colophon Your Rights As A Reader Dedication Preface What This Book Is About Who Should Read This Book Before You Read This Book How To Read This Book Code Examples Errata About the Authors About the Translators Introduction 1. Rediscovering Simplicity 1.1. Simplifying Code 1.1.1. Incomprehensibly Concise Consistency Duplication Names 1.1.2. Speculatively General 1.1.3. Concretely Abstract 1.1.4. Shameless Green 1.2. Judging Code 1.2.1. Evaluating Code Based on Opinion 1.2.2. Evaluating Code Based on Facts Source Lines of Code Cyclomatic Complexity Assignments, Branches and Conditions (ABC) Metric 1.2.3. Comparing Solutions 1.3. Summary 2. Test Driving Shameless Green
📄 Page 3
2.1. Understanding Testing 2.2. Writing the First Test 2.3. Removing Duplication 2.4. Tolerating Duplication 2.5. Hewing to the Plan 2.6. Exposing Responsibilities 2.7. Choosing Names 2.8. Revealing Intentions 2.9. Writing Cost-Eective Tests 2.10. Avoiding the Echo-Chamber 2.11. Considering Options 2.12. Summary 3. Unearthing Concepts 3.1. Listening to Change 3.2. Starting With the Open/Closed Principle 3.3. Recognizing Code Smells 3.4. Identifying the Best Point of Attack 3.5. Refactoring Systematically 3.6. Following the Flocking Rules 3.7. Converging on Abstractions 3.7.1. Focusing on Dierence 3.7.2. Simplifying Hard Problems 3.7.3. Naming Concepts 3.7.4. Making Methodical Transformations 3.7.5. Refactoring Gradually 3.8. Summary 4. Practicing Horizontal Refactoring 4.1. Replacing Dierence With Sameness 4.2. Equivocating About Names 4.3. Deriving Names From Responsibilities 4.4. Choosing Meaningful Defaults 4.5. Seeking Stable Landing Points 4.6. Obeying the Liskov Substitution Principle 4.7. Taking Bigger Steps 4.8. Discovering Deeper Abstractions 4.9. Depending on Abstractions
📄 Page 4
4.10. Summary 5. Separating Responsibilities 5.1. Selecting the Target Code Smell 5.1.1. Identifying Patterns in Code 5.1.2. Spotting Common Qualities 5.1.3. Enumerating Flocked Method Commonalities 5.1.4. Insisting Upon Messages 5.2. Extracting Classes 5.2.1. Modeling Abstractions 5.2.2. Naming Classes 5.2.3. Extracting BottleNumber 5.2.4. Removing Arguments 5.2.5. Trusting the Process 5.3. Appreciating Immutability 5.4. Assuming Fast Enough 5.5. Creating BottleNumbers 5.6. Recognizing Liskov Violations 5.7. Summary 6. Achieving Openness 6.1. Consolidating Data Clumps 6.2. Making Sense of Conditionals 6.3. Replacing Conditionals with Polymorphism 6.3.1. Dismembering Conditionals 6.3.2. Manufacturing Objects 6.3.3. Prevailing with Polymorphism 6.4. Transitioning Between Types 6.5. Making the Easy Change 6.6. Defending the Domain 6.7. Summary 7. Manufacturing Intelligence 7.1. Contrasting the Concrete Factory with Shameless Green 7.2. Fathoming Factories 7.3. Opening the Factory 7.4. Supporting Arbitrary Class Names 7.5. Dispersing The Choosing Logic 7.6. Self-registering Candidates
📄 Page 5
7.7. Summary 8. Developing a Programming Aesthetic 8.1. Appreciating the Mechanical Process 8.2. Clarifying Responsibilities with Pseudocode 8.3. Extracting the Verse 8.4. Coding by Wishful Thinking 8.5. Inverting Dependencies 8.5.1. Injecting Dependencies 8.5.2. Isolating Variants 8.5.3. Grappling with Inversion 8.6. Obeying the Law of Demeter 8.6.1. Understanding the Law 8.6.2. Curing Demeter Violations 8.7. Identifying What The Verse Method Wants 8.8. Pushing Object Creation to the Edge 8.9. Summary 9. Reaping the Benets of Design 9.1. Choosing Which Units to Test 9.1.1. Contrasting Unit and Integration Tests 9.1.2. Foregoing Tests 9.2. Reorganizing Tests 9.2.1. Gathering BottleVerse Tests 9.2.2. Revealing Intent 9.3. Seeking Context Independence 9.3.1. Examining Bottles' Responsibilities 9.3.2. Purifying Tests With Fakes 9.3.3. Purging Redundant Tests 9.3.4. Proting from Loose Coupling 9.4. Communicating With the Future 9.4.1. Enriching Code with Signals 9.4.2. Verifying Roles 9.4.3. Obliterating Obsolete Context 9.5. Summary Afterword Appendix A: Initial Exercise Getting the exercise
📄 Page 6
Doing the exercise Test Suite References Acknowledgements
📄 Page 7
Colophon Colophon Version: 2.0.0 Version Date: 20-07-12 Published By: Potato Canyon Software, LLC 2nd Edition Copyright: 2020 Cover Design and Art by Lindsey Morris. Edited by Julia Trimmer. Created using Asciidoctor. JavaScript logo: License, MIT
📄 Page 8
Your Rights As A Reader Your Rights As A Reader Thank you for buying 99 Bottles of OOP. As the authors of a self-published book, we very much appreciate your purchase. This book is chock-full of lessons, and readers often write asking if they can share them with others. We commend your desire to pass on what you’ve learned, and ask only that while doing so you respect our rights are authors. This means that while we encourage you to spread the underlying ideas of the book, we restrict your use of its actual content (the specific examples, explanations, and descriptions). Our bargain with you is as follows: 1. Your purchase entitles you to a single, non-transferable license for your personal use of the ebook related files. You may read and download the ebook you purchased to your personal devices only. You may not: Sell the book Give it away Distribute it in any way Print it (except for your personal use) 2. You may use 99 Bottles of OOP as curriculum in a public education setting (university, code school, secondary school) as long as every student buys or is provided with a legal copy of the book. Volume discounts are available, and there’s a free-book-for-a-postcard program. Contact human@99bottlesbook.com for information about bulk purchases. 3. You may share one small section (a chapter or less) at a free, public meet-up as long as the material is properly attributed. 4. You may not teach a course based on the entire book, even if this course is free and open to the public. 5. You may not use any part of 99 Bottles of OOP in any endeavor in which you charge for your services.
📄 Page 9
Dedication Dedication Sandi To Amy, for everything she is and does, and to Jasper, who taught me that nothing trumps a good walk. Katrina To Sander, whose persistence is out of this world. TJ To those who have encouraged me, especially my parents and teachers. And to Graylyn, who I try to encourage most.
📄 Page 10
What This Book Is About Preface It turns out that everything you need to know about Object-Oriented Design (OOD) can be learned from the "99 Bottles of Beer" song. Well, perhaps not everything, but quite certainly a great many things. The song is simultaneously easy to understand and full of hidden complexity, which makes it the perfect skeleton upon which to hang lessons in OOD. The lessons embedded within the song are so useful, and so broad, that over the last three years it has become a core part of the curriculum of Sandi Metz’s Practical Object-Oriented Design course. The thoughts in this book reflect countless hours of discussion and collaboration between Sandi, Katrina Owen, and TJ Stankus. These ideas have been battle-tested by hundreds of students, and refined by a series of deeply thoughtful co-instructors, beginning with Katrina. While none of the authors have the hubris to claim perfect understanding, all have learned a great deal about Object-Oriented Design from teaching this song, and feel compelled to write it all down. Therefore, this book, now in its second edition. We hope that you find it both useful and enjoyable. What This Book Is About This book is about writing cost-effective, maintainable, and pleasing code. Chapter 1 explores how to decide if code is "good enough." This chapter uses metrics to compare several possible solutions to the 99 Bottles problem. It introduces a type of solution known as Shameless Green, and argues that although Shameless Green is neither clever nor changeable, it is the best initial solution to many problems. Chapter 2 is a primer for Test-Driven Development (TDD), which is used to find Shameless Green. This chapter is concerned with deciding what to test, and with creating tests that happily tolerate changes to the underlying code. Chapter 3 introduces a new requirement (six-pack), which leads to a discussion of how to decide where to start when changing code. This chapter examines the Open/Closed Principle, and then explores code smells. The chapter then defines a simple set of Flocking Rules, which guide a step- by-step refactoring of code. Chapter 4 continues the step-by-step refactoring begun in Chapter 3. It iteratively applies the Flocking Rules, eventually stumbles across the need for the Liskov Substitution Principle, and ultimately unearths a deeply hidden abstraction. Chapter 5 inventories the existing code for smells, chooses the most prominent one, and uses it to trigger the creation of a new class. Along the way, it takes a hard look at immutability, performance, and caching.
📄 Page 11
Who Should Read This Book Chapter 6 performs a miracle that not only removes the conditionals, but also allows you to finally implement the new six-pack requirement without altering existing code. Chapter 7 examines the tradeoffs along a continuum of six different styles of Factories. It begins by exploring a simple, hard-coded conditional, and ends with a factory whose candidate objects both self-register, and also supply the logic needed to choose them. Chapter 8 introduces another new requirement—to vary the lyrics. It uses this requirement to introduce the idea of a programming aesthetic, or set of rules to guide you in times of uncertainty. The chapter ends with a list of specific suggestions for deciding when it’s worthwhile to voluntarily improve code. Chapter 9 comes full circle and returns to testing. It takes advantage of the improved design to write better tests, and then uses the new tests as a spur to improve the final design. Who Should Read This Book The lessons in the book have been found useful by programmers with a broad range of experience, from rank novice through grizzled veteran. Despite what one might predict, novices often have an easier time with this material. As they are unencumbered by prior knowledge, their minds are open, and easily absorb these ideas. It’s the veterans who struggle. Their habits are deeply ingrained. They know themselves to be good at programming. They feel quick, and efficient, and so resist new techniques, especially when those techniques temporarily slow them down. This book will be useful if you are a veteran, but it cannot be denied that it teaches programming techniques that likely contradict your current practice. Changing entrenched ideas can be painful. However, you cannot make informed decisions about the value of new ideas unless you thoroughly understand them, and to understand them you must commit, wholeheartedly, to learning them. Therefore, if you are a veteran, it’s best to adopt the novice mindset before reading on. Set aside prior beliefs, and dedicate yourself to what follows. While reading, resist the urge to resist. Read the entire book, work the problems, and only then decide whether to integrate these ideas into your daily practice. Before You Read This Book You’ll learn more from this book if you spend 30 minutes working on the "99 Bottles of Beer" problem before starting to read. See the appendix for instructions. If you just want to read on but you don’t know JavaScript, have no fear. Your purchase of this book entitles you to variants in Ruby, Javascript, and (soon-to-be-released) PHP, and each language variant comes in beer and milk flavors. Every combination of variant and flavor was available for download during your purchase, and one of them should suit.
📄 Page 12
How To Read This Book Regardless of which version you read, be assured that this book is not about any specific language or beverage; it’s about object-oriented programming and design. The technical content of every version is essentially the same. How To Read This Book The chapters build upon one another, and so should be read in order. While isolated sections may be useful, the whole is more than the sum of its parts. The ideas gain power in relation to one another. To get the most from the book, work the code samples as you read along. With active participation, you’ll learn more, understand better, and retain longer. While reading has value, doing has more. Code Examples The code examples in this version of the book are written in Javascript. The source code shown in the book is on GitHub. The majority of code listings are extracted from this repository; and for those, the listing numbers link to the associated code in the repo. The exercises rely on Jest. Errata A current list of errata is located at sandimetz.com/99bottles-errata. If you find additional errors, please email them to errata@99bottlesbook.com. About the Authors Sandi Metz Sandi is the author of Practical Object-Oriented Design in Ruby. She has thirty years of experience working on large object-oriented applications. She’s spoken about programming, object-oriented design and refactoring at numerous conferences including Agile Alliance Technical Conference, Craft Conf, Øredev, RailsConf, and RubyConf. She believes in simple code and straightforward explanations, and is the proud recipient of a Ruby Hero award for her contribution to the Ruby community. She prefers working software, practical solutions and lengthy bicycle trips (not necessarily in that order). Find out more about Sandi at sandimetz.com. Katrina Owen Katrina works for GitHub as an Advocate on the Open Source team. Katrina has ten years of experience and works primarily in Go and Ruby. She is the creator of exercism.io, a platform for programming skill development in more than 30 languages. She’s spoken about refactoring and open source at international conferences such as NordicRuby, Mix-IT, Software Craftsmanship North America, OSCON, Bath Ruby and RailsConf. She received a Ruby Hero award for her contribution to the Ruby community. When programming, her focus is on automation, workflow optimization, and refactoring. Find out more about Katrina at kytrinyx.com.
📄 Page 13
About the Translators TJ Stankus TJ works as a software developer for boldfacet LLC and co-instructs software design courses with Sandi. He began his programming career over 20 years ago by accident. By hacking together WordPerfect macros to streamline his job as a proofreader, he discovered he loved programming as much as any creative activity he’d ever pursued. He has worked in mobile applications and back-end web development. He even wrote an SMTP server back when that seemed like a good idea. Today, he works primarily with Elixir and Ruby. His main interests lie not in specific programming languages, but in the essential design ideas that span programming languages and paradigms. Find out more about TJ at tj.stank.us. About the Translators Tom Stuart The JavaScript translation was done by Tom Stuart. Tom is a computer scientist, programmer and technical leader. He’s the former CTO of FutureLearn and Econsultancy. He has lectured on optimising compilers at the University of Cambridge and written about technology for the Guardian. Tom is the proud author of Understanding Computation, which was published by O’Reilly in 2013.
📄 Page 14
Introduction Introduction This book creates a simple solution to the "99 Bottles of Beer" song problem, and then applies a series of refactorings to improve the design of the code. Put that way, the topic sounds so painfully obvious that one might reasonably wonder if this entire tome could be replaced by a few samples of code. These refactoring "end points" would be a fraction of the size of this book, and a vastly quicker read. Unfortunately, they would teach you almost nothing about programming. Writing code is the process of working your way to the next stable end point, not the end point itself. You don’t know the answer in advance, but instead, you are seeking it. This book documents every step down every path of code, and so provides a guided-tour of the decisions made along the way. It not only shows how good code looks when it’s done, it reveals the thoughts that produced it. It aims to leave nothing out. It flings back the veil behind which sausage is being made. One final note before diving into the book proper. The chapters that follow apply a general, broad solution to a specific, narrow problem. The authors cheerfully stipulate the fact that you are unlikely to encounter the "99 Bottles of Beer" song in your daily work, and that problems of similar size are best solved very simply. For the purposes of this book, "99 Bottles" is convenient because it’s simultaneously easily understandable and surprisingly complex, and so provides an expedient stand-in for larger problems. Once you understand the solutions here, you’ll be able to apply them to the much larger real world. With that, on to the book.
📄 Page 15
1. Rediscovering Simplicity Page 1 1. Rediscovering Simplicity When you were new to programming you wrote simple code. Although you may not have appreciated it at the time, this was a great strength. Since then, you’ve learned new skills, tackled harder problems, and produced increasingly complex solutions. Experience has taught you that most code will someday change, and you’ve begun to craft it in anticipation of that day. Complexity seems both natural and inevitable. Where you once optimized code for understandability, you now focus on its changeability. Your code is less concrete but more abstract—you’ve made it initially harder to understand in hopes that it will ultimately be easier to maintain. This is the basic promise of Object-Oriented Design (OOD): that if you’re willing to accept increases in the complexity of your code along some dimensions, you’ll be rewarded with decreases in complexity along others. OOD doesn’t claim to be free; it merely asserts that its benefits outweigh its costs. Design decisions inevitably involve trade-offs. There’s always a cost. For example, if you’ve duplicated a bit of code in many places, the Don’t Repeat Yourself (DRY) principle tells you to extract the duplication into a single common method and then invoke this new method in place of the old code. DRY is a great idea, but that doesn’t mean it’s free. The price you pay for DRYing out code is that the invoker of the new method no longer knows the result, only the message it should send. If you’re willing to pay this price, that is, you are willing to be ignorant of the actual behavior, the reward you reap is that when the behavior changes, you need alter your code in only one place. The argument that OOD makes is that this bargain will save you money. Did you divide one large class into many small ones? You can now reuse the new classes independently of one another, but it’s no longer obvious how they fit together for the original case. Have you injected a dependency instead of hard-coding the class name of a collaborator? The receiver can now freely depend on new and previously unforeseen objects, but it must remain ignorant of their actual class. The examples above change code by increasing its level of abstraction. DRYing out code inserts a level of indirection between the place that uses behavior and the place that defines it. Breaking one large class into many forces the creation of something new to embody the relationship between the pieces. Injecting a dependency transforms the receiver into something that depends on an abstract role rather than a concrete class. Each of these design choices has costs, and it only makes sense to pay these costs if you also accrue some offsetting benefits. Design is thus about picking the right abstractions. If you choose well, your code will be expressive, understandable and flexible, and everyone will love both it and you. However, if you get the abstractions wrong, your code will be convoluted, confusing, and costly, and your programming peers will hate you. Unfortunately, abstractions are hard, and even with the best of intentions, it’s easy to get them wrong. Well-meaning programmers tend to over-anticipate abstractions, inferring them
📄 Page 16
1.1.1. Incomprehensibly Concise Page 2 prematurely from incomplete information. Early abstractions are often not quite right, and therefore they create a catch-22.[1] You can’t create the right abstraction until you fully understand the code, but the existence of the wrong abstraction may prevent you from ever doing so. This suggests that you should not reach for abstractions, but instead, you should resist them until they absolutely insist upon being created. This book is about finding the right abstraction. This first chapter starts by peeling away the fog of complexity and defining what it means to write simple code. 1.1. Simplifying Code The code you write should meet two often-contradictory goals. It must remain concrete enough to be understood while simultaneously being abstract enough to allow for change. Imagine a continuum with "most concrete" at one end and "most abstract" at the other. Code at the concrete end might be expressed as a single long procedure full of if statements. Code at the abstract end might consist of many classes, each with one method containing a single line of code. The best solution for most problems lies not at the extremes of this continuum, but somewhere in the middle. There’s a sweet spot that represents the perfect compromise between comprehension and changeability, and it’s your job as a programmer to find it. This section discusses four different solutions to the "99 Bottles of Beer" problem. These solutions vary in complexity and thus illustrate different points along this continuum. You must now make a decision. As you were forewarned in the preface, the best way to learn from this book is to work the exercises yourself. If you continue reading before solving the problem in your own way, your ideas will be contaminated by the code that follows. Therefore, if you plan to work along, go do the 99 Bottles exercise now. When you’re finished, you’ll be ready to examine the following four solutions. 1.1.1. Incomprehensibly Concise Here’s the first of four different solutions to the "99 Bottles" song. Listing 1.1: Incomprehensibly Concise 1 class Bottles { 2 song() { 3 return this.verses(99, 0); 4 } 5 6 verses(hi, lo) { 7 return downTo(hi, lo).map(n => this.verse(n)).join('\n'); 8 } 9 10 verse(n) { 11 return ( 12 `${n === 0 ? 'No more' : n} bottle${n === 1 ? '' : 's'}` + 13 ' of beer on the wall, ' + 14 `${n === 0 ? 'no more' : n} bottle${n === 1 ? '' : 's'} of beer.\n` +
📄 Page 17
1.1.1. Incomprehensibly Concise Page 3 15 `${n > 0 ? `Take ${n > 1 ? 'one' : 'it'} down and pass it around` 16 : 'Go to the store and buy some more'}, ` + 17 `${n-1 < 0 ? 99 : n-1 === 0 ? 'no more' : n-1} bottle${n-1 === 1 ? '' : 's'}`+ 18 ' of beer on the wall.\n' 19 ); 20 } 21 } 22 23 // Here is the definition of the downTo helper function 24 // used above. It will be omitted from subsequent listings. 25 26 const downTo = (max, min) => { 27 const numbers = []; 28 for (let n = max; n >= min; n--) { 29 numbers.push(n); 30 } 31 return numbers; 32 }; This first solution embeds a great deal of logic into the verse string. The code above performs a neat trick. It manages to be concise to the point of incomprehensibility while simultaneously retaining loads of duplication. This code is hard to understand because it is inconsistent and duplicative, and because it contains hidden concepts that it does not name. Consistency The style of the conditionals is inconsistent. Most use the ternary form, as on line 12: n === 0 ? 'No more' : n Finally, there’s the ternary within a ternary on line 17, which is best left without comment: n-1 < 0 ? 99 : n-1 === 0 ? 'no more' : n-1 Every time the style of the conditionals changes, the reader has to press a mental reset button and start thinking anew. Inconsistent styling makes code harder for humans to parse; it raises costs without providing benefits. Duplication The code duplicates both data and logic. Having multiple copies of the strings "of beer" and "on the wall" isn’t great, but at least string duplication is easy to see and understand. Logic, however, is harder to comprehend than data, and duplicated logic is doubly so. Of course, if you want to achieve maximum confusion, you can interpolate duplicated logic inside strings, as does the verse method above. For example, "bottle" pluralization is done in three places. The code to do this is identical in two of the places, on Lines 12 and 14: n === 1 ? '' : 's' But later, on line 17, the pluralization logic is subtly different. Suddenly it’s not n that matters, but n-1: n-1 === 1 ? '' : 's'
📄 Page 18
1.1.1. Incomprehensibly Concise Page 4 Duplication of logic suggests that there are concepts hidden in the code that are not yet visible because they haven’t been isolated and named. The need to sometimes say "bottle" and other times say "bottles" means something, and the need to sometimes use n and other times use n-1 means something else. The code gives no clue about what these meanings might be; you’re left to figure this out for yourself. Names The most obvious point to be made about the names in the verse method of Listing 1.1: Incomprehensibly Concise is that there aren’t any. The verse string contains embedded logic. Each bit of logic serves some purpose, and it is up to you to construct a mental map of what these purposes might be. This code would be easier to understand if it did not place that burden upon you, the intrepid reader. The logic that’s hidden inside the verse string should be dispersed into methods, and verse should fill itself with values by sending messages. Terminology: Method versus Message A "method" is defined on an object, and contains behavior. In the previous example, the Bottles class defines a method named song. A "message" is sent by an object to invoke behavior. In the aforementioned example, the song method sends the verses message to the receiver this. Therefore, methods are defined, and messages are sent. The confusion between these terms comes about because it is common for the receiver of a message to define a method whose name exactly corresponds to that message. Consider the example above. The song method sends the verses message to this, which results in an invocation of the verses method. The fact that the message name and the method name are identical may make it seem as if the terms are synonymous. They are not. Think of objects as black boxes. Methods are defined within a black box. Messages are passed between them. There are many ways for an object to cheerfully respond to a message for which it does not define a matching method. While it is common for message names to map directly to method names, there is no requirement that this be so. Drawing a distinction between messages and methods improves your OO mindset. It allows you to isolate the intention of the sender from the implementation in the receiver. OO promises that if you send the right message, the correct behavior will occur, regardless of the names of the methods that eventually get invoked. Creating a method requires identifying the code you’d like to extract and deciding on a method name. This, in turn, requires naming the concept, and naming things is just plain hard. In the case
📄 Page 19
1.1.1. Incomprehensibly Concise Page 5 above, it’s especially hard. This code not only contains many hidden concepts, but those concepts are mixed together, conflated, such that their individual natures are obscured. Combining many ideas into a small section of code makes it difficult to isolate and name any single concept. When you first write a piece of code, you obviously know what it does. Therefore, during initial development, the price you pay for poor names is relatively low. However, code is read many more times than it is written, and its ultimate cost is often very high and paid by someone else. Writing code is like writing a book; your efforts are for other readers. Although the struggle for good names is painful, it is worth the effort if you wish your work to survive to be read. Code clarity is built upon names. Problems with consistency, duplication, and naming conspire to make the code in Listing 1.1: Incomprehensibly Concise likely to be costly. Note that the above assertion is, at this point, an unsupported opinion. The best way to judge code would be to compare its value to its cost, but unfortunately it’s hard to get good data. Judgments about code are therefore commonly reduced to individual opinion, and humans are not always in accord. There’s no perfect solution to this problem, but the Judging Code section, later in this chapter, suggests ways to acquire empirical data about the goodness of code. Independent of all judgment about how well a bit of code is arranged, code is also charged with doing what it’s supposed to do now as well as being easy to alter so that it can do more later. While it’s difficult to get exact figures for value and cost, asking the following questions will give you insight into the potential expense of a bit of code: 1. How difficult was it to write? 2. How hard is it to understand? 3. How expensive will it be to change? The past ("was it") is a memory, the future ("will it be") is imaginary, but the present ("is it") is true right now. The very act of looking at a piece of code declares that you wish to understand it at this moment. Questions 1 and 3 above may or may not concern you, but question 2 always applies. Code is easy to understand when it clearly reflects the problem it’s solving, and thus openly exposes that problem’s domain. If Listing 1.1: Incomprehensibly Concise openly exposed the "99 Bottles" domain, a brief glance at the code would answer these questions: 1. How many verse variants are there? 2. Which verses are most alike? In what way? 3. Which verses are most different, and in what way? 4. What is the rule to determine which verse comes next? These questions reflect core concepts of the problem, yet none of their answers are apparent in this solution. The number of variants, the difference between the variants, and the algorithm for looping are distressingly obscure. This code does not reflect its domain, and therefore you can
📄 Page 20
1.1.2. Speculatively General Page 6 infer that it was difficult to write and will be a challenge to change. If you had to characterize the goal of the writer of Listing 1.1: Incomprehensibly Concise, you might suggest that their highest priority was brevity. Brevity may be the soul of wit, but it quickly becomes tedious in code. Incomprehensible conciseness is clearly not the best solution for the "99 Bottles" problem. It’s time to examine one that’s more verbose. 1.1.2. Speculatively General This next solution errs in a different direction. It does many things well but can’t resist indulging in unnecessary complexity. Have a look at the code below: Listing 1.2: Speculatively General 1 const NoMore = verse => 2 'No more bottles of beer on the wall, ' + 3 'no more bottles of beer.\n' + 4 'Go to the store and buy some more, ' + 5 '99 bottles of beer on the wall.\n'; 6 7 const LastOne = verse => 8 '1 bottle of beer on the wall, ' + 9 '1 bottle of beer.\n' + 10 'Take it down and pass it around, ' + 11 'no more bottles of beer on the wall.\n'; 12 13 const Penultimate = verse => 14 '2 bottles of beer on the wall, ' + 15 '2 bottles of beer.\n' + 16 'Take one down and pass it around, ' + 17 '1 bottle of beer on the wall.\n'; 18 19 const Default = verse => 20 `${verse.number} bottles of beer on the wall, ` + 21 `${verse.number} bottles of beer.\n` + 22 'Take one down and pass it around, ' + 23 `${verse.number - 1} bottles of beer on the wall.\n`; 24 25 class Bottles { 26 song() { 27 return this.verses(99, 0); 28 } 29 30 verses(finish, start) { 31 return downTo(finish, start) 32 .map(verseNumber => this.verse(verseNumber)) 33 .join('\n'); 34 } 35 36 verse(number) { 37 return this.verseFor(number).text(); 38 } 39 40 verseFor(number) { 41 switch (number) { 42 case 0: return new Verse(number, NoMore); 43 case 1: return new Verse(number, LastOne); 44 case 2: return new Verse(number, Penultimate); 45 default: return new Verse(number, Default); 46 } 47 } 48 }
The above is a preview of the first 20 pages. Register to read the complete e-book.

💝 Support Author

0.00
Total Amount (¥)
0
Donation Count

Login to support the author

Login Now

Recommended for You

Loading recommended books...
Failed to load, please try again later
Back to List