Skip to content

Commit 1336a65

Browse files
committed
1 parent f0224a7 commit 1336a65

27 files changed

+609
-415
lines changed

compose-method/README.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
---
2+
layout: pattern
3+
title: Compose-method
4+
folder: compose-method
5+
permalink: /patterns/compose-method/
6+
categories: Idiom
7+
tags:
8+
- Decoupling
9+
---
10+
11+
## Intent
12+
13+
Transform the logic into a small number of intention-revealing steps at the same level of detail.
14+
15+
## Explanation
16+
17+
Real world example
18+
19+
> to make the method easy to read.
20+
21+
In plain words
22+
23+
> Composed Method is a small, simple method that you can understand in seconds.
24+
25+
Wikipedia says
26+
27+
> In software engineering, composed method is to divide your program into methods that perform one identifiable task. Keep all of the operations in a method at the same level of abstraction. This will naturally result in programs with many small methods, each a few lines long.
28+
29+
**Programmatic Example**
30+
31+
[Refactoring to Patterns: Simplification | Compose Method | InformIT](https://www.informit.com/articles/article.aspx?p=1398607)
32+
33+
The original method is relative hard to understand the meaning.
34+
```java
35+
public class List{
36+
public void add(Object element){
37+
if(!readOnly){
38+
int newSize = size + 1;
39+
if (newSize > elements.length) {
40+
Object[] newElements =
41+
new Object[elements.length + 10];
42+
for (int i = 0; i < size; i++)
43+
newElements[i] = elements[i];
44+
elements = newElements;
45+
}
46+
elements[size++] = element;
47+
}
48+
}
49+
}
50+
```
51+
52+
After use a guard clause, we can make an early exit from the method.
53+
54+
```java
55+
public class List{
56+
public void add(Object element) {
57+
if (readOnly)
58+
return;
59+
int newSize = size + 1;
60+
if (newSize > elements.length) {
61+
Object[] newElements =
62+
new Object[elements.length + 10];
63+
for (int i = 0; i < size; i++)
64+
newElements[i] = elements[i];
65+
elements = newElements;
66+
}
67+
elements[size++] = element;
68+
}
69+
}
70+
```
71+
72+
change the constant to be more communicative.
73+
74+
```java
75+
public class List{
76+
private final static int GROWTH_INCREMENT = 10;
77+
78+
public void add(Object element) {
79+
if (readOnly)
80+
return;
81+
int newSize = size + 1;
82+
if (newSize > elements.length) {
83+
Object[] newElements =
84+
new Object[elements.length + GROWTH_INCREMENT];
85+
for (int i = 0; i < size; i++)
86+
newElements[i] = elements[i];
87+
elements = newElements;
88+
}
89+
elements[size++] = element;
90+
}
91+
}
92+
```
93+
94+
Finally, extract each part of the code as the methods.
95+
96+
```java
97+
public class List{
98+
private final static int GROWTH_INCREMENT = 10;
99+
100+
public void add(Object element) {
101+
if (readOnly)
102+
return;
103+
if (atCapacity())
104+
grow();
105+
addElement(element);
106+
}
107+
108+
private boolean atCapacity() {
109+
return (size + 1) > elements.length;
110+
}
111+
112+
private void grow() {
113+
Object[] newElements =
114+
new Object[elements.length + GROWTH_INCREMENT];
115+
for (int i = 0; i < size; i++)
116+
newElements[i] = elements[i];
117+
elements = newElements;
118+
}
119+
120+
private void addElement(Object element) {
121+
elements[size++] = element;
122+
}
123+
}
124+
```
125+
126+
## Applicability
127+
128+
Use the Compose method pattern when
129+
130+
* You want to make the content of the method easy to read.
131+
* You want to set each method with one function.
132+
133+
## Real world examples
134+
135+
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
136+
137+
## Credits
138+
139+
* [Refactoring to Patterns: Simplification](https://www.informit.com/articles/article.aspx?p=1398607)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@startuml
2+
package com.iluwatar.composemethod {
3+
class AddImpl {
4+
+ AddImpl()
5+
+ addPositiveNodesOriginal(head : Node) : int {static}
6+
+ addPositiveNodesRefined(head : Node) : int {static}
7+
+ grow(nums : int[]) : int {static}
8+
+ isPositive(a : int) : boolean {static}
9+
+ lengthDetect(tail : Node) : boolean {static}
10+
+ nextNode(tail : Node) : Node {static}
11+
+ nodeDetect(tail : Node) : boolean {static}
12+
+ numGrow(adder : int) : int {static}
13+
+ numberGrowth(tail : Node) : int {static}
14+
}
15+
class App {
16+
- LOGGER : Logger {static}
17+
+ App()
18+
+ main(args : String[]) {static}
19+
}
20+
class Node {
21+
~ next : Node
22+
~ nums : int[]
23+
+ Node()
24+
}
25+
}
26+
Node --> "-next" Node
27+
@enduml

facet/pom.xml renamed to compose-method/pom.xml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</parent>
1010
<modelVersion>4.0.0</modelVersion>
1111

12-
<artifactId>facet</artifactId>
12+
<artifactId>compose-method</artifactId>
1313
<dependencies>
1414
<dependency>
1515
<groupId>org.junit.jupiter</groupId>
@@ -27,9 +27,5 @@
2727
</plugin>
2828
</plugins>
2929
</build>
30-
<properties>
31-
<maven.compiler.source>11</maven.compiler.source>
32-
<maven.compiler.target>11</maven.compiler.target>
33-
</properties>
3430

3531
</project>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.iluwatar.composemethod;
2+
3+
/**
4+
* implement adding.
5+
*/
6+
public class AddImpl { //NOPMD
7+
/**
8+
* default added number.
9+
*/
10+
private static final int DEFAULT_NUMBER = 0;
11+
12+
/**
13+
* original add method.
14+
*
15+
* @param head the head of the list.
16+
* @return the sum of positive numbers.
17+
*/
18+
public static int addPositiveNodesOriginal(final Node head) {
19+
Node tail = head;
20+
int rtn = 0;
21+
while (tail != null) {
22+
if (tail.nums.length > 0) {
23+
for (int i = 0; i < tail.nums.length; i++) {
24+
if (tail.nums[i] > 0) {
25+
rtn += tail.nums[i];
26+
}
27+
}
28+
}
29+
tail = tail.next;
30+
}
31+
return rtn;
32+
}
33+
34+
/**
35+
* refined adding method.
36+
*
37+
* @param head the head of the list.
38+
* @return the sum of the positive number.
39+
*/
40+
public static int addPositiveNodesRefined(final Node head) {
41+
Node tail = head;
42+
int rtn = 0;
43+
while (nodeDetect(tail)) {
44+
rtn += numberGrowth(tail);
45+
tail = nextNode(tail);
46+
}
47+
return rtn;
48+
}
49+
50+
/**
51+
* point to the next node of the current node.
52+
*
53+
* @param tail the current node.
54+
* @return the next node of the current node.
55+
*/
56+
public static Node nextNode(final Node tail) {
57+
return tail.next;
58+
}
59+
60+
/**
61+
* add a list of positive number or skip when the list is empty.
62+
*
63+
* @param tail the current node.
64+
* @return the added result.
65+
*/
66+
public static int numberGrowth(final Node tail) {
67+
int result;
68+
if (lengthDetect(tail)) {
69+
result = grow(tail.nums); //NOPMD
70+
} else {
71+
result = DEFAULT_NUMBER; //NOPMD
72+
}
73+
return result;
74+
}
75+
76+
/**
77+
* check whether the node is null.
78+
*
79+
* @param tail current node.
80+
* @return whether it is null.
81+
*/
82+
public static boolean nodeDetect(final Node tail) {
83+
return tail != null;
84+
}
85+
86+
/**
87+
* check the length of list is positive.
88+
*
89+
* @param tail the current node.
90+
* @return whether its list is positive.
91+
*/
92+
public static boolean lengthDetect(final Node tail) {
93+
return tail.nums.length > 0;
94+
}
95+
96+
/**
97+
* compute the total sum of the list of positive numbers.
98+
*
99+
* @param nums the list of numbers.
100+
* @return the sum of the numbers.
101+
*/
102+
public static int grow(int[] nums) { //NOPMD
103+
int rtn = 0;
104+
for (final int x :
105+
nums) {
106+
rtn += numGrow(x);
107+
}
108+
return rtn;
109+
}
110+
111+
/**
112+
* judge whether the number is positive.
113+
*
114+
* @param adder the number for judgement.
115+
* @return if number is positive, return it. Otherwise, return default number.
116+
*/
117+
public static int numGrow(final int adder) {
118+
int result;
119+
if (isPositive(adder)) {
120+
result = adder;
121+
} else {
122+
result = DEFAULT_NUMBER;
123+
}
124+
return result;
125+
}
126+
127+
/**
128+
* check the number is positive.
129+
*
130+
* @param adder the number.
131+
* @return whethr is positive.
132+
*/
133+
public static boolean isPositive(final int adder) {
134+
return adder > 0;
135+
}
136+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.iluwatar.composemethod;
2+
3+
import java.security.SecureRandom;
4+
import java.util.Random;
5+
import lombok.extern.slf4j.Slf4j;
6+
7+
/**
8+
* add original method is rough, and the new
9+
* add method is using the composed method.
10+
* In this example, refactor the method to
11+
* let each method contain only one function
12+
* and ensure the refactored method's result
13+
* is equals to the original method.
14+
* {@link Node} is the node for list, and
15+
* this term is to add the positive integer in
16+
* each node's integer list
17+
*/
18+
@Slf4j
19+
public class App { //NOPMD
20+
/**
21+
* Program entry point.
22+
*
23+
* @param args command line args.
24+
*/
25+
public static void main(final String[] args) {
26+
//initialize a list.
27+
final var head = new Node();
28+
LOGGER.info("head: {}", head);
29+
final SecureRandom random = new SecureRandom();
30+
final int nodes = random.nextInt(10);
31+
var tail = head; //NOPMD
32+
33+
//for each node to initialize the integer list.
34+
head.nums = new int[random.nextInt(10)];
35+
for (int j = 0; j < head.nums.length; j++) {
36+
head.nums[j] = random.nextInt(20) - 10;
37+
}
38+
for (int i = 0; i < nodes; i++) {
39+
tail.next = new Node(); //NOPMD
40+
tail = tail.next;
41+
tail.nums = new int[random.nextInt(10)]; //NOPMD
42+
for (int j = 0; j < tail.nums.length; j++) {
43+
tail.nums[j] = random.nextInt(20) - 10;
44+
}
45+
}
46+
LOGGER.info("original method result: {}", AddImpl.addPositiveNodesOriginal(head));
47+
LOGGER.info("refined method result: {}", AddImpl.addPositiveNodesRefined(head));
48+
}
49+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.iluwatar.composemethod;
2+
3+
/**
4+
* the data structure to create list.
5+
*/
6+
public class Node { //NOPMD
7+
/**
8+
* the list of numbers.
9+
*/
10+
public int[] nums;
11+
/**
12+
* the pointer of the next node.
13+
*/
14+
public Node next;
15+
}

0 commit comments

Comments
 (0)