Skip to content

Commit edb412c

Browse files
committed
realize compose method pattern.
1 parent af0ccdc commit edb412c

File tree

7 files changed

+369
-0
lines changed

7 files changed

+369
-0
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: Structural
7+
tags:
8+
- Gang of Four
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
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)

compose-method/pom.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>java-design-patterns</artifactId>
7+
<groupId>com.iluwatar</groupId>
8+
<version>1.25.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>compose-method</artifactId>
13+
<dependencies>
14+
<dependency>
15+
<groupId>org.junit.jupiter</groupId>
16+
<artifactId>junit-jupiter-api</artifactId>
17+
<version>5.4.2</version>
18+
<scope>test</scope>
19+
</dependency>
20+
</dependencies>
21+
22+
<properties>
23+
<maven.compiler.source>11</maven.compiler.source>
24+
<maven.compiler.target>11</maven.compiler.target>
25+
</properties>
26+
27+
</project>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.iluwatar.composemethod;
2+
3+
/**
4+
* implement adding.
5+
*/
6+
public class AddImpl {
7+
private static final int DEFAULT_NUMBER = 0;
8+
9+
/**
10+
* original add method.
11+
*
12+
* @param head the head of the list.
13+
* @return the sum of positive numbers.
14+
*/
15+
public static int addPositiveNodesOriginal(Node head) {
16+
Node tail = head;
17+
int rtn = 0;
18+
while (tail != null) {
19+
if (tail.nums.length > 0) {
20+
for (int i = 0; i < tail.nums.length; i++) {
21+
if (tail.nums[i] > 0) {
22+
rtn += tail.nums[i];
23+
}
24+
}
25+
}
26+
tail = tail.next;
27+
}
28+
return rtn;
29+
}
30+
31+
/**
32+
* refined adding method.
33+
*
34+
* @param head the head of the list.
35+
* @return the sum of the positive number.
36+
*/
37+
public static int addPositiveNodesRefined(Node head) {
38+
Node tail = head;
39+
int rtn = 0;
40+
while (nodeDetect(tail)) {
41+
rtn += numberGrowth(tail);
42+
tail = nextNode(tail);
43+
}
44+
return rtn;
45+
}
46+
47+
/**
48+
* point to the next node of the current node.
49+
*
50+
* @param tail the current node.
51+
* @return the next node of the current node.
52+
*/
53+
public static Node nextNode(Node tail) {
54+
return tail.next;
55+
}
56+
57+
/**
58+
* add a list of positive number or skip when the list is empty.
59+
*
60+
* @param tail the current node.
61+
* @return the added result.
62+
*/
63+
public static int numberGrowth(Node tail) {
64+
if (!lengthDetect(tail)) {
65+
return DEFAULT_NUMBER;
66+
}
67+
return grow(tail.nums);
68+
}
69+
70+
public static boolean nodeDetect(Node tail) {
71+
return tail != null;
72+
}
73+
74+
public static boolean lengthDetect(Node tail) {
75+
return tail.nums.length > 0;
76+
}
77+
78+
/**
79+
* compute the total sum of the list of positive numbers.
80+
*
81+
* @param nums the list of numbers.
82+
* @return the sum of the numbers.
83+
*/
84+
public static int grow(int[] nums) {
85+
int rtn = 0;
86+
for (int i = 0; i < nums.length; i++) {
87+
rtn += numGrow(nums[i]);
88+
}
89+
return rtn;
90+
}
91+
92+
/**
93+
* judge whether the number is positive.
94+
*
95+
* @param adder the number for judgement.
96+
* @return if number is positive, return it. Otherwise, return default number.
97+
*/
98+
public static int numGrow(int adder) {
99+
if (isPositive(adder)) {
100+
return adder;
101+
}
102+
return DEFAULT_NUMBER;
103+
}
104+
105+
public static boolean isPositive(int a) {
106+
return a > 0;
107+
}
108+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.iluwatar.composemethod;
2+
3+
import java.util.Random;
4+
import lombok.extern.slf4j.Slf4j;
5+
6+
/**
7+
* add original method is rough, and the new add method is using the composed method.
8+
* {@link Node} is the node for list, and this term is to add the positive integer in
9+
* each node's integer list
10+
*/
11+
@Slf4j
12+
public class App {
13+
/**
14+
* Program entry point.
15+
*
16+
* @param args command line args.
17+
*/
18+
public static void main(String[] args) {
19+
//initialize a list.
20+
Node head = new Node();
21+
LOGGER.info("head: {}", head);
22+
Random random = new Random();
23+
int nodes = random.nextInt(10);
24+
Node tail = head;
25+
26+
//for each node to initialize the integer list.
27+
head.nums = new int[random.nextInt(10)];
28+
for (int j = 0; j < head.nums.length; j++) {
29+
head.nums[j] = random.nextInt(20) - 10;
30+
}
31+
for (int i = 0; i < nodes; i++) {
32+
tail.next = new Node();
33+
tail = tail.next;
34+
tail.nums = new int[random.nextInt(10)];
35+
for (int j = 0; j < tail.nums.length; j++) {
36+
tail.nums[j] = random.nextInt(20) - 10;
37+
}
38+
}
39+
LOGGER.info("original method result: {}", AddImpl.addPositiveNodesOriginal(head));
40+
LOGGER.info("refined method result: {}", AddImpl.addPositiveNodesRefined(head));
41+
}
42+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.iluwatar.composemethod;
2+
3+
/**
4+
* the data structure to create list.
5+
*/
6+
public class Node {
7+
int[] nums;
8+
Node next;
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.iluwatar.composemethod;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
6+
7+
public class AppTest {
8+
@Test
9+
void shouldExecuteWithoutException() {
10+
assertDoesNotThrow(() -> App.main(new String[]{}));
11+
}
12+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.iluwatar.composemethod;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.Random;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class MethodEqualTest {
10+
@Test
11+
void methodsEqual() {
12+
Node head = new Node();
13+
Random random = new Random();
14+
int nodes = random.nextInt(10);
15+
Node tail = head;
16+
17+
//for each node to initialize the integer list.
18+
head.nums = new int[random.nextInt(10)];
19+
for (int j = 0; j < head.nums.length; j++) {
20+
head.nums[j] = random.nextInt(20) - 10;
21+
}
22+
for (int i = 0; i < nodes; i++) {
23+
tail.next = new Node();
24+
tail = tail.next;
25+
tail.nums = new int[random.nextInt(10)];
26+
for (int j = 0; j < tail.nums.length; j++) {
27+
tail.nums[j] = random.nextInt(20) - 10;
28+
}
29+
}
30+
assertEquals(AddImpl.addPositiveNodesRefined(tail), AddImpl.addPositiveNodesOriginal(tail));
31+
}
32+
}

0 commit comments

Comments
 (0)