Today's programs are more complicated than ever. Overwhelming detail threatens to prevent developers from writing code that can be understood and safely changed. Exactly how to organize all this detail is a significant challenge. Luckily there is something that developers can learn from journalists.
The Inverted Pyramid is a writing style that journalists use to organize the details in newspaper articles. It provides two important benefits for readers. First, readers can leave an article at any point and understand it, even if they do not have all the details. Second, for those readers who wish to proceed, it conducts them through the article details.
8. “The Lead”
The Lead
“The Body”
“The Tail”
“The Lead”: the most
important information
“The Body”: the crucial
information
9. “The Lead”
The Lead
“The Body”
“The Tail”
“The Lead”: the most
important information
“The Body”: the crucial
information
“The Tail”: any extra
information
10. The Lead
Benefits for Readers
1. Readers can leave an article at any point
and understand it, even if they do not
have all the details.
2. For those readers who wish to proceed, it
conducts them through the article details.
13. Some Boos at Graduation After Judge Bars Prayer
Associated Press
May 21, 2001
WASHINGTON, Ill. - A top student who gave a traditional farewell
speech at a high school graduation was booed and another student
was applauded for holding a moment of silence after a judge barred
prayer at the ceremony.
•
•
•
•
22
2
2
Experience inverted pyramid
Read one card at a time
You have 4 minutes
It’s not important to read all cards
14. Some Boos at Graduation After Judge Bars Prayer
Associated Press
May 21, 2001
“The Lead”
WASHINGTON, Ill. - A top student who gave a traditional farewell speech at a high school graduation was
booed and another student was applauded for holding a moment of silence after a judge barred prayer at
the ceremony.
A federal judge issued a restraining order days before Sunday's ceremony at Washington Community
High School blocking any student-led prayer. It was the first time in the 80-year history of the school that
no graduation prayers were said.
The Lead
“The Body”
Natasha Appenheimer, the class valedictorian, traditionally a top student chosen to give the class
graduation speech, was booed when she received her diploma. Her family, backed by the American Civil
Liberties Union, had filed the lawsuit that led to the restraining order. Meanwhile, some stood and
applauded class speaker Ryan Brown when he bowed his head for a moment of silence before his
speech.
About 200 people attended a prayer vigil before the ceremony, and a placard-carrying atheist and a
Pentecostal minister got into a shouting match.
In spite of the turbulent atmosphere, Appenheimer said she wasn't upset by the way things turned out.
“The Tail”
"It's my graduation. I'm happy," she said. The lawsuit "was worth it. We changed things, we righted a
wrong and made something better than it was before. I learned that when you believe in something, you
should stand up for it."
Graduate Annie White disagreed, saying many class members wanted to demonstrate that "God was a
part of our graduation."
Superintendent Lee Edwards said the school district might appeal McDade's ruling. He said the
invocation and benediction prayers usually said at the ceremony were innocuous, and "You would have to
have been working pretty hard to be offended."
School district officials defended the prayer on grounds that students, not administrators, were in charge
of graduation.
The Supreme Court's landmark 1962 decision outlawed organized prayer in public schools. In 1992, the
justices barred clergy-led prayers at graduations, and last year, the court barred officials from letting
students lead crowds in prayer before football games.
15. public class StringCalculator {
!
!
!
!
!
public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
failWhenContainsNegatives(values);
return sumOf(values);
}
private String[] parseValues(String numbers) {
if (numbers.isEmpty()) {
return new String[] {};
}
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValues(numbers);
}
!
!
!
!
!
}
private List<Integer> convertToInteger(String[] numbers) {
List<Integer> result = new ArrayList<>();
!
!
!
}
private boolean containsCustomDelimiters(String numbers) {
return numbers.startsWith("//");
}
private String[] parseCustomDelimitedValues(String numbers) {
int newlineIndex = numbers.indexOf('n');
String delimiters = numbers.substring(2, newlineIndex);
String numberList = numbers.substring(newlineIndex + 1);
String[] customDelimiters = parseCustomDelimiters(delimiters);
!
!
for (String customDelimiter : customDelimiters) {
numberList = numberList.replaceAll(
quoteRegularExpression(customDelimiter), ",");
}
!
return result;
}
private void failWhenContainsNegatives(List<Integer> values) {
List<Integer> negatives = new ArrayList<>();
for (Integer value : values) {
if (value < 0) negatives.add(value);
}
if (!negatives.isEmpty()) {
String message = "Error: negatives not allowed " + negatives;
throw new RuntimeException(message);
}
}
return result;
for (String number : numbers) {
result.add(toInteger(number));
}
!
for (Integer value : values) {
result += value;
}
!
return parseStandardDelimiters(numbers);
!
!
private int sumOf(List<Integer> values) {
int result = 0;
!
!
!
!
!
}
return numberList.split(",");
}
private String[] parseCustomDelimiters(String rawCustomDelimiters) {
return rawCustomDelimiters.replaceAll("[", "").split("]");
}
private String quoteRegularExpression(String customSeparator) {
return "Q" + customSeparator + "E";
}
private String[] parseStandardDelimiters(String numbers) {
return numbers.split("[,n]");
}
private Integer toInteger(String number) {
Integer value = Integer.parseInt(number);
return value <= 1000 ? value : 0;
}
16. Benefits for Readers
public class StringCalculator {
!
!
!
The Lead
!
public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
private int
int
!
for
result += value;
failWhenContainsNegatives(values);
return sumOf(values);
}
}
!
!
return
}
1. Readers can leave an article at any point
!
!
!
and understand it, even if they do not
!
have all the details.
!
!
private String[] parseValues(String numbers) {
if (numbers.isEmpty()) {
return
}
private
return
}
private
int
if (containsCustomDelimiters(numbers)) {
return
}
String delimiters = numbers.substring(2, newlineIndex);
String numberList = numbers.substring(newlineIndex + 1);
return parseStandardDelimiters(numbers);
}
!
String[] customDelimiters = parseCustomDelimiters(delimiters);
private List<Integer> convertToInteger(String[] numbers) {
List<Integer> result =
for
for (String number : numbers) {
result.add(toInteger(number));
}
numberList = numberList.replaceAll(
quoteRegularExpression(customDelimiter),
}
!
!
2. For those readers who wish to proceed, it
!
!
conducts them through the article details.
!
!
return
}
return result;
}
private
return
}
private void failWhenContainsNegatives(List<Integer> values) {
List<Integer> negatives =
for (Integer value : values) {
if (value < 0) negatives.add(value);
}
!
if (!negatives.isEmpty()) {
String message =
throw
}
}
!
!
!
!
}
private
return
}
private
return
}
private
Integer value = Integer.parseInt(number);
return
}
17. The Lead
Benefits for Developers
1. Readers can leave an article at any point
and understand it, even if they do not
have all the details.
2. For those readers who wish to proceed, it
conducts them through the article details.
18. The Lead
Benefits for Developers
1. Developers can leave source code at any
point and understand it, even if they do
not have all the details.
2. For those readers who wish to proceed, it
conducts them through the article details.
19. The Lead
Benefits for Developers
1. Developers can leave source code at any
point and understand it, even if they do
not have all the details.
2. For those developers who need to see
more implementation details, it conducts
them through the source code.
20. The Lead
Benefits for Developers
1. Understanding without all the details
2. Details are effectively organized
25. public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
failWhenContainsNegatives(values);
return sumOf(values);
}
Body
Body
Body
Tail
Tail
Tail
•
•
Flip over the card labelled Lead
•
Share that understanding with your group
What can you say about the add()
method from reading the Lead card ?
26. public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
failWhenContainsNegatives(values);
private String[] parseValues(String numbers) {
if (numbers.isEmpty()) {
return new String[] {};
}
return sumOf(values);
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValues(numbers);
}
}
Body
Body
return parseStandardDelimiters(numbers);
}
Tail
Tail
Tail
•
•
Flip over the Body cards one at a time
•
•
Next, flip over the Tail cards
Ask yourself: What do I notice about the
code after reading each card ?
It’s not important to read all cards
27. public class StringCalculator {
!
!
!
!
!
public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
failWhenContainsNegatives(values);
return sumOf(values);
}
private String[] parseValues(String numbers) {
if (numbers.isEmpty()) {
return new String[] {};
}
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValues(numbers);
}
!
!
!
!
!
}
private List<Integer> convertToInteger(String[] numbers) {
List<Integer> result = new ArrayList<>();
!
!
!
}
private boolean containsCustomDelimiters(String numbers) {
return numbers.startsWith("//");
}
private String[] parseCustomDelimitedValues(String numbers) {
int newlineIndex = numbers.indexOf('n');
String delimiters = numbers.substring(2, newlineIndex);
String numberList = numbers.substring(newlineIndex + 1);
String[] customDelimiters = parseCustomDelimiters(delimiters);
!
!
for (String customDelimiter : customDelimiters) {
numberList = numberList.replaceAll(
quoteRegularExpression(customDelimiter), ",");
}
!
return result;
}
private void failWhenContainsNegatives(List<Integer> values) {
List<Integer> negatives = new ArrayList<>();
for (Integer value : values) {
if (value < 0) negatives.add(value);
}
if (!negatives.isEmpty()) {
String message = "Error: negatives not allowed " + negatives;
throw new RuntimeException(message);
}
}
return result;
for (String number : numbers) {
result.add(toInteger(number));
}
!
for (Integer value : values) {
result += value;
}
!
return parseStandardDelimiters(numbers);
!
!
private int sumOf(List<Integer> values) {
int result = 0;
!
!
!
!
!
}
return numberList.split(",");
}
private String[] parseCustomDelimiters(String rawCustomDelimiters) {
return rawCustomDelimiters.replaceAll("[", "").split("]");
}
private String quoteRegularExpression(String customSeparator) {
return "Q" + customSeparator + "E";
}
private String[] parseStandardDelimiters(String numbers) {
return numbers.split("[,n]");
}
private Integer toInteger(String number) {
Integer value = Integer.parseInt(number);
return value <= 1000 ? value : 0;
}
28. public class StringCalculator {
!
public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
!
failWhenContainsNegatives(values);
!
return sumOf(values);
}
29. public class StringCalculator {
!
public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
!
failWhenContainsNegatives(values);
!
return sumOf(values);
}
32. Compose Method
!
You can’t rapidly understand
a method’s logic.
!
Transform the logic into a
small number of intentionrevealing steps at the same
level of detail.
34. Composed Method and SLAP
!
Composed method
encourages factoring (or
refactoring) code into small,
cohesive, readable chunks.
SLAP stands for the Single
Level of Abstraction Principle.
36. public int add(String numbers) {
List<Integer> negatives = new ArrayList<>();
int sum = 0;
for (String number : parseValues(numbers)) {
Integer value = Integer.parseInt(number);
if (value < 0) {
negatives.add(value);
} else if (value <= 1000) {
sum += value;
}
}
if (!negatives.isEmpty()) {
throw new RuntimeException("Error: negatives not allowed " +
negatives);
}
return sum;
}
37. public int add(String numbers) {
List<Integer> negatives = new ArrayList<>();
int sum = 0;
for (String number : parseValues(numbers)) {
Integer value = Integer.parseInt(number);
if (value < 0) {
negatives.add(value);
} else if (value <= 1000) {
sum += value;
}
}
if (!negatives.isEmpty()) {
throw new RuntimeException("Error: negatives not allowed " +
negatives);
}
return sum;
}
You can’t rapidly understand
a method’s logic.
38. public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
!
failWhenContainsNegatives(values);
!
return sumOf(values);
}
Compose methods out of
calls to other methods, each
of which is at roughly the
same level of abstraction.
39. public int add(String numbers) {
List<Integer> values = convertToInteger(parseValues(numbers));
!
failWhenContainsNegatives(values);
!
return sumOf(values);
}
Understanding without all the detail
The Lead
Details are effectively organized
46. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (numbers.startsWith("//")) {
return parseCustomDelimitedValues(numbers);
}
!
return numbers.split("[,n]");
}
47. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (numbers.startsWith("//")) {
return parseCustomDelimitedValues(numbers);
}
!
return numbers.split("[,n]");
}
SLAP stands for the Single
Level of Abstraction Principle.
48. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (numbers.startsWith("//")) {
return parseCustomDelimitedValues(numbers);
}
!
return numbers.split("[,n]");
}
SLAP stands for the Single
Level of Abstraction Principle.
49. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (numbers.startsWith("//")) {
return parseCustomDelimitedValues(numbers);
}
!
return numbers.split("[,n]");
}
50. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValues(numbers);
}
!
return numbers.split("[,n]");
}
51. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValues(numbers);
}
!
return parseStandardDelimiters(numbers);
}
52. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValues(numbers);
}
!
return parseStandardDelimiters(numbers);
}
SLAP stands for the Single
Level of Abstraction Principle.
53. private String[] parseValues(String numbers) {
if (numbers.isEmpty())
return new String[] {};
!
if (containsCustomDelimiters(numbers)) {
return parseCustomDelimitedValue(numbers);
}
!
return parseStandardDelimiters(numbers);
}
Understanding without all the detail
The Lead
Details are effectively organized
57. Compose Method
Liabilities
• Can lead to an overabundance of small methods.
• Can make debugging difficult because logic is
spread out across many small methods.
Refactoring to Patterns
58. Compose Method
Benefits
•
Efficiently communicates what a method does
and how it does what it does.
•
Simplifies a method by breaking it up into
well-named chunks of behaviour at the
same level of detail.
Refactoring to Patterns
60. The Lead
Benefits for Developers
1. Developers can leave source code at any
point and understand it, even if they do
not have all the details.
2. For those developers who need to see
more implementation details, it conducts
them through the source code.