├── .gitignore
├── README.md
├── WorkingEffectivelyWithLegacyCodeExamples.iml
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── out
└── .gitignore
└── src
├── main
└── java
│ └── com
│ └── thoughtworks
│ └── legacycode
│ ├── adapt_parameter
│ ├── Main.java
│ └── ReportGenerator.java
│ ├── break_out_method_object
│ ├── Main.java
│ ├── Monster.java
│ ├── Physics.java
│ └── Rendering.java
│ ├── encapsulate_global_reference
│ ├── Book.java
│ ├── Library.java
│ └── Main.java
│ ├── exercise
│ ├── Defragger.java
│ ├── DiskUtils.java
│ ├── FileManager.java
│ ├── FilePrinter.java
│ ├── Main.java
│ ├── Partition.java
│ └── PartitionDoesNotExistException.java
│ ├── expose_static_method
│ ├── Book.java
│ ├── Library.java
│ └── Main.java
│ ├── extract_and_override_call
│ ├── Book.java
│ ├── Library.java
│ └── Main.java
│ ├── parameterize_constructor
│ ├── Animals.java
│ ├── Enclosure.java
│ ├── Main.java
│ ├── Zoo.java
│ └── ZooManager.java
│ ├── parameterize_method
│ ├── Book.java
│ ├── Library.java
│ └── Main.java
│ ├── primitivize_parameter
│ ├── Main.java
│ ├── Monster.java
│ ├── MoveToMeetingPointCommand.java
│ ├── Physics.java
│ ├── Rendering.java
│ └── Vector2.java
│ ├── pull_up_feature
│ ├── Main.java
│ ├── Monster.java
│ ├── Physics.java
│ └── Rendering.java
│ ├── push_down_dependency
│ ├── Main.java
│ ├── Monster.java
│ ├── Physics.java
│ └── Rendering.java
│ ├── remove_duplication
│ ├── Animal.java
│ ├── Cat.java
│ ├── Dog.java
│ ├── Duplication.java
│ └── Fish.java
│ ├── spawn_class
│ ├── Main.java
│ └── Monster.java
│ ├── spawn_method
│ ├── Main.java
│ └── Monster.java
│ ├── subclass_and_override_method
│ ├── AiEntity.java
│ ├── Main.java
│ ├── Monster.java
│ ├── Physics.java
│ ├── Rendering.java
│ └── TestMonster.java
│ ├── wrap_class
│ ├── GameEntity.java
│ ├── Main.java
│ ├── Monster.java
│ ├── Physics.java
│ └── Rendering.java
│ └── wrap_method
│ ├── Main.java
│ └── Monster.java
└── test
└── java
└── com
└── thoughtworks
└── legacycode
├── adapt_parameter
└── ReportGeneratorTest.java
├── break_out_method_object
└── AiEntityTest.java
├── encapsulate_global_reference
└── LibraryTest.java
├── expose_static_method
└── LibraryTest.java
├── extract_and_override_call
└── LibraryTest.java
├── parameterize_constructor
└── ZooManagerTest.java
├── parameterize_method
└── LibraryTest.java
├── primitivize_parameter
└── MoveToMeetingPointCommandTest.java
├── pull_up_feature
└── AiEntityTest.java
├── push_down_dependency
└── AiEntityTest.java
├── spawn_class
└── AiEntityTest.java
├── spawn_method
└── AiEntityTest.java
├── subclass_and_override_method
└── AiEntityTest.java
├── wrap_class
└── AiEntityTest.java
└── wrap_method
└── AiEntityTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.class
3 | .gradle/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WorkingEffectivelyWithLegacyCode
2 | ================================
3 |
4 | ## Why do we change software?
5 | 1. Add a feature - Add some behavior, hold existing behavior constant
6 | 1. Fix a bug - Change some behavior, hold other behavior constant
7 | 1. Improve design - Change code structure, hold existing behavior constant
8 | 1. Optimize resource usage - Change code to improve resource usage, hold existing behavior constant
9 |
10 | Detecting changes in existing behavior is important!
11 |
12 | # Legacy Code
13 | Legacy code is code without tests. -- *Michael Feathers*
14 |
15 | ## The Legacy Code Dilemma
16 | When we change code, we should have tests in place. To put tests in place, we often have to change code.
17 |
18 | ## The Legacy Code Change Algorithm
19 | 1. Identify Change Points
20 | 1. Find Test Points
21 | 1. Break Dependencies
22 | 1. Write Tests
23 | 1. Make Changes and Refactor
24 |
25 | ### How do I know that I'm not breaking anything?
26 |
27 | #### Rules
28 | 1. Hyper-aware Editing
29 | 1. Single Goal Editing
30 | 1. Preserve Signatures
31 | 1. Lean on the Compiler
32 | 1. Pair Programming
33 |
34 | #### Seams
35 | A seam is a place where you can alter behavior in your program without editing in that place.
36 |
37 | ##### Characterization Tests
38 | * *Characterizes* the actual behavior of the code.
39 | * Use white box testing to identify useful input values
40 | * Assert on the current actual results
41 |
42 | ##### Interception Points
43 | * An interception point is simply a point in your program where you can detect the effects of a particular change. Make this as close to your change points as you can.
44 |
45 | #### Process
46 | * Automated Refactoring to introduce basic seams and break dependencies
47 | * Cover with Characterization Tests and Regular Unit Tests
48 | * Introduce seams at the change and interception points using less safe refactorings (if needed)
49 | * TDD change
50 |
51 | ## Breaking Dependencies
52 | ### Sensing & Separation
53 |
54 | We break dependencies:
55 | * so we can *sense* when we can't access values our code computes
56 | * to *separate* when we can't even get a piece of code into a test harness to run.
57 |
58 | #### Sensing
59 | * *verify*
60 | * getters and non-private fields
61 |
62 | #### Separation
63 | * *when*
64 | * avoid using real resources
65 | * helps write maintainable tests
66 |
67 | ## Principles
68 | #### In order to make code better, we sometimes need to make some aspect of it worse.
69 | Testable & Clear > **Testable & Muddy** > **Untestable & Clear** > Untestable & Muddy
70 |
71 | _When you break dependencies in legacy code, you often have to suspend your sense of aesthetics a bit. Some
72 | dependencies break cleanly; others end up looking less than ideal from a design point of view. They are like the
73 | incision point in surgery: There might be a scar left in your code after your work, but everything beneath it can get better._
74 |
75 | _If later you can cover the code around the point where you broke the dependencies, you can heal the scar too._
76 |
77 | #### We have to carefully balance these priorities
78 | * New features
79 | * Design Improvements
80 | * Tests
81 |
82 | ## Practices
83 | ### Techniques for Breaking Dependencies
84 | #### Parameterize Constructor
85 | * Inject a dependency instead of leaving it internal to a class
86 |
87 | #### Parameterize Method
88 | * Inject a dependency instead of leaving it internal to a method
89 |
90 | #### Spawn Method
91 | * Introduce a method and TDD that
92 |
93 | #### Spawn Class
94 | * Introduce a class and TDD that
95 |
96 | #### Break out Method Object
97 | * Extract method you want to change into a new class and test that
98 |
99 | #### Subclass and Override Method
100 | * Test a subclass of your real class and override methods with dependencies
101 |
102 | #### Extract and Override Call
103 | * Extract tough dependency and override it then test child class
104 |
105 | #### Extract and Override Factory Method (No example yet)
106 | * Move constructor dependency to a method and override it
107 |
108 | #### Pull up Feature
109 | * Pull the parts of a class you want to test into a new abstract base class then test a child of that
110 |
111 | #### Push down Dependencies
112 | * Make current class abstract and push your dependencies into a child class. Test through a test child class.
113 |
114 | #### Expose Static Method
115 | * Change existing method to be static (if it can be). You can test without an instance
116 |
117 | #### Wrap Method
118 | * Introduce a method that contains an existing method and a call to you new method
119 |
120 | #### Wrap Class aka Decorator
121 | * Wrap your hard to test class with a Decorator and TDD the decorator
122 |
123 | #### Adapt Parameter
124 | * Use adapter pattern on tough dependency
125 |
126 | #### Encapsulate Global Reference
127 | * Introduce new class that holds your global which it exposes with a getter
128 |
129 | #### Introduce Instance Delegator (No example yet)
130 | * Introduce a new class that contains related global methods
131 |
132 | #### Primitivize Parameter
133 | * Pass the values from an object instead of the object
134 |
135 | #### Introduce Static Setter (No example yet)
136 | * Add a setInstance to your existing Singleton (**Danger!**)
137 |
138 | ### Supporting Concepts
139 |
140 | #### Scratch Refactoring
141 | * Refactor the code to understand it better, *then throw it away*.
142 | * Use only automated refactorings, *then check it in*. <- Bill's version
143 |
144 | #### Removing Duplication
145 | * Use automated refactorings to make different code blocks identical
146 | * Extract method or variable (IDE does the rest)
147 | * Do example
148 |
149 | #### Monster Methods
150 | * Bulleted Method - indentation is not the most obvious problem
151 | * Snarled Method - indentation makes you dizzy
152 |
153 | #### Command/Query Separation
154 | * Modify state or report state. Getters should be idempotent
155 |
156 |
157 |
--------------------------------------------------------------------------------
/WorkingEffectivelyWithLegacyCodeExamples.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 |
3 | sourceCompatibility = 1.7
4 | version = '1.0'
5 |
6 | repositories {
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | compile 'joda-time:joda-time:2.7'
12 | testCompile 'junit:junit:4.11'
13 | testCompile 'org.mockito:mockito-core:1.10.19'
14 | testCompile 'org.hamcrest:hamcrest-core:1.3'
15 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThoughtWorksInc/WorkingEffectivelyWithLegacyCode/07d97efded14c73f18f50767b95964ccb5b3f6a7/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Oct 07 10:58:45 CDT 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn ( ) {
37 | echo "$*"
38 | }
39 |
40 | die ( ) {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/out/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThoughtWorksInc/WorkingEffectivelyWithLegacyCode/07d97efded14c73f18f50767b95964ccb5b3f6a7/out/.gitignore
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/adapt_parameter/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.adapt_parameter;
2 |
3 | import java.io.Console;
4 | import java.io.InputStream;
5 |
6 | public class Main {
7 | public static void main(String[] args) {
8 | final ReportGenerator reportGenerator = new ReportGenerator();
9 | final Console console = System.console();
10 | reportGenerator.reportToConsole(console);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/adapt_parameter/ReportGenerator.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.adapt_parameter;
2 |
3 | import java.io.Console;
4 |
5 | public class ReportGenerator {
6 |
7 | // Change the console parameter to your new adapter class
8 | public void reportToConsole(Console console) {
9 | final String userInput = console.readLine();
10 | console.printf(userInput);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/break_out_method_object/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.break_out_method_object;
2 |
3 | public class Main {
4 | public static void main(String[] args) {
5 | new Monster(new Physics(), new Rendering(), 10, "Angry").processAi();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/break_out_method_object/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.break_out_method_object;
2 |
3 | public class Monster {
4 |
5 | private Physics physics;
6 | private Rendering rendering;
7 | private Integer hitPoints;
8 | private String state;
9 | // many more instance variables
10 |
11 |
12 | public Monster(Physics physics, Rendering rendering, Integer hitPoints, String state) {
13 | this.physics = physics;
14 | this.rendering = rendering;
15 | this.hitPoints = hitPoints;
16 | this.state = state;
17 | this.physics.add(this);
18 | this.rendering.render(this);
19 | }
20 |
21 | // Move this method to a new class and test that method
22 | public void processAi(){
23 | if (hitPoints > 5){
24 | if (state.equals("Angry")){
25 | moveToNearestEnemy();
26 | attack();
27 | } else if (state.equals("Afraid")){
28 | moveAwayFromNearestEnemy();
29 | }
30 | } else {
31 | rest();
32 | }
33 | }
34 |
35 | private void rest() {
36 | // lots of code here
37 | // none of it uses physics or rendering
38 | }
39 |
40 | private void moveAwayFromNearestEnemy() {
41 | // lots of code here
42 | // none of it uses physics or rendering
43 | }
44 |
45 | private void attack() {
46 | // lots of code here
47 | // none of it uses physics or rendering
48 | }
49 |
50 | private void moveToNearestEnemy() {
51 | // lots of code here
52 | // none of it uses physics or rendering
53 | }
54 |
55 | public void state(String newState) {
56 | state = newState;
57 | }
58 |
59 | public Integer hitPoints() {
60 | return hitPoints;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/break_out_method_object/Physics.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.break_out_method_object;
2 |
3 | public final class Physics {
4 | public static Physics getInstance() {
5 | return null;
6 | }
7 |
8 | public void add(Monster monster) {
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/break_out_method_object/Rendering.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.break_out_method_object;
2 |
3 | public final class Rendering {
4 | public void render(Monster monster) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/encapsulate_global_reference/Book.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.encapsulate_global_reference;
2 |
3 | /**
4 | * Created by ThoughtWorker on 4/7/14.
5 | */
6 | public class Book {
7 | private boolean overDue=false;
8 | private String name;
9 |
10 | public Book(String name) {
11 |
12 | this.name = name;
13 | }
14 |
15 | public boolean isOverDue() {
16 | return overDue;
17 | }
18 |
19 | public String getName() {
20 | return name;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/encapsulate_global_reference/Library.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.encapsulate_global_reference;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class Library {
7 | public void listBooks() {
8 | for (Book book : Main.BookList) {
9 | System.out.println(book.getName());
10 | }
11 | }
12 |
13 | public void listOverdueBooks() {
14 | List overdueBooks = new ArrayList();
15 | for (Book book : Main.BookList) {
16 | if (book.isOverDue()){
17 | overdueBooks.add(book);
18 | }
19 | }
20 | for (Book overdueBook : overdueBooks) {
21 | System.out.println(overdueBook.getName());
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/encapsulate_global_reference/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.encapsulate_global_reference;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | public class Main {
7 | public static List BookList = Arrays.asList(new Book("The Two Towers"), new Book("House of Leaves"));
8 |
9 | public static void main(String[] args) {
10 | Library library = new Library();
11 | library.listBooks();
12 | library.listOverdueBooks();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/Defragger.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | public class Defragger {
4 | private String[] p;
5 |
6 | public Defragger(String[] p) {
7 | this.p = p;
8 | }
9 |
10 | public boolean canDefrag(Partition p) {
11 | for(String n : this.p){
12 | if (n.equals(p)){
13 | return true;
14 | }
15 | }
16 | return false;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/DiskUtils.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | import java.io.File;
4 |
5 | public class DiskUtils {
6 | private static DiskUtils instance = null;
7 |
8 | public void format(String input) {
9 | // code that formats a physical hard drive partition
10 |
11 | }
12 |
13 | public static DiskUtils getInstance() {
14 | if (instance == null){
15 | instance = new DiskUtils();
16 | }
17 | return instance;
18 | }
19 |
20 | public File getFile(String fn) {
21 | return new File(fn);
22 | }
23 |
24 | public void useDefragger(FileManager fm, Defragger defragger) throws PartitionDoesNotExistException {
25 | String pn = fm.input("Enter partition name");
26 | if (defragger.canDefrag(getPartition(pn))){
27 |
28 | }
29 |
30 | }
31 |
32 | private Partition getPartition(String n) throws PartitionDoesNotExistException {
33 | return new Partition(n);
34 | }
35 |
36 | public boolean canFindPartition(String n) {
37 | // Looks for partition on disk and returns true if it exists
38 | return false;
39 | }
40 | public boolean canFindFile(String n) {
41 | // Looks for file on disk and returns true if it exists
42 | return false;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/FileManager.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 |
8 | public class FileManager {
9 | private static Defragger df;
10 | private String input = null;
11 | private BufferedReader br = null;
12 |
13 | public static void run() throws PartitionDoesNotExistException {
14 | FileManager fm = new FileManager();
15 | while(!fm.input("Enter disk command").equals("quit")){
16 | String input1 = fm.getInput();
17 | if(input1.equals("format")) {
18 | DiskUtils.getInstance().format(fm.input("Enter name of drive to format"));
19 | }
20 | else if(input1.equals("print")) {
21 | String fn = fm.input("Enter file name");
22 | File file = DiskUtils.getInstance().getFile(fn);
23 | FilePrinter fp = new FilePrinter(file);
24 | fp.print();
25 | }
26 | else if (input1.equals("defrag")){
27 | DiskUtils.getInstance().useDefragger(fm, df);
28 | }
29 | }
30 | }
31 |
32 | public String getInput() {
33 | if (input == null){
34 | input = input();
35 | }
36 | return input;
37 | }
38 |
39 | private String input() {
40 | if (br == null){
41 | br = new BufferedReader(new InputStreamReader(System.in));
42 | }
43 | try {
44 | return br.readLine();
45 | } catch (IOException e) {
46 | e.printStackTrace();
47 | }
48 | return "";
49 | }
50 |
51 | public String input(String prompt) {
52 | System.out.println(prompt);
53 | if (br == null){
54 | br = new BufferedReader(new InputStreamReader(System.in));
55 | }
56 | try {
57 | return br.readLine();
58 | } catch (IOException e) {
59 | e.printStackTrace();
60 | }
61 | return "";
62 | }
63 |
64 | public static void init(String[] partitions) {
65 | df = new Defragger(partitions);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/FilePrinter.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | import java.io.File;
4 |
5 | public class FilePrinter {
6 | public FilePrinter(File file) {
7 |
8 | }
9 |
10 | public void print() {
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | public class Main {
4 |
5 | public static void main(String[] args) throws PartitionDoesNotExistException {
6 | String[] partitions = {"Partition1", "Partition2"};
7 | FileManager.init(partitions);
8 | FileManager.run();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/Partition.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | public class Partition {
4 | public Partition(String n) throws PartitionDoesNotExistException {
5 | if (!DiskUtils.getInstance().canFindPartition(n)){
6 | throw new PartitionDoesNotExistException();
7 | }
8 | // initialize partition
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/exercise/PartitionDoesNotExistException.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.exercise;
2 |
3 | public class PartitionDoesNotExistException extends Exception {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/expose_static_method/Book.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.expose_static_method;
2 |
3 | public class Book {
4 | private boolean overDue;
5 | private String name;
6 |
7 | public Book(String name, boolean overdue) {
8 | this.name = name;
9 | this.overDue = overdue;
10 | }
11 |
12 | public boolean isOverDue() {
13 | return overDue;
14 | }
15 |
16 | public String getName() {
17 | return name;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/expose_static_method/Library.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.expose_static_method;
2 |
3 | import java.io.Console;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | public class Library {
8 |
9 | private final String libraryName;
10 |
11 | public Library(Console console) {
12 | // We need to avoid calling this line in our tests. So we need to not call the constructor
13 | libraryName = console.readLine();
14 | }
15 |
16 | public void printBooks(List books) {
17 | System.out.println(libraryName);
18 | for (Book book : books) {
19 | System.out.println(book);
20 | }
21 | }
22 |
23 | // Make this static so you can test it without instantiating Library
24 | public List overdueBooks(List bookList) {
25 | List overdueBooks = new ArrayList();
26 | for (Book book : bookList) {
27 | if (book.isOverDue()){
28 | overdueBooks.add(book);
29 | }
30 | }
31 | return overdueBooks;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/expose_static_method/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.expose_static_method;
2 |
3 | import java.util.List;
4 |
5 | import static java.util.Arrays.asList;
6 |
7 | public class Main {
8 | public static void main(String[] args) {
9 | List bookList = asList(new Book("The Two Towers", true), new Book("House of Leaves", true));
10 | final Library library = new Library(System.console());
11 | library.printBooks(bookList);
12 | final List overdueBooks = library.overdueBooks(bookList);
13 | library.printBooks(overdueBooks);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/extract_and_override_call/Book.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.extract_and_override_call;
2 |
3 | public class Book {
4 | private String name;
5 |
6 | public Book(String name) {
7 | this.name = name;
8 | }
9 |
10 | public String getName() {
11 | return name;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/extract_and_override_call/Library.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.extract_and_override_call;
2 |
3 | import java.io.PrintStream;
4 | import java.util.List;
5 |
6 | public class Library {
7 | private PrintStream printStream;
8 |
9 | public Library(PrintStream printStream) {
10 | this.printStream = printStream;
11 | }
12 |
13 | public void printBooks(List books) {
14 |
15 | // Extract this line into a protected method and override it in an new subclass
16 | final String libraryName = System.console().readLine();
17 |
18 | printStream.println(libraryName);
19 | for (Book book : books) {
20 | printStream.println(book.getName());
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/extract_and_override_call/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.extract_and_override_call;
2 |
3 | import java.util.List;
4 |
5 | import static java.util.Arrays.asList;
6 |
7 | public class Main {
8 | public static void main(String[] args) {
9 | List bookList = asList(new Book("The Two Towers"), new Book("House of Leaves"));
10 | final Library library = new Library(System.out);
11 | library.printBooks(bookList);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_constructor/Animals.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_constructor;
2 |
3 | import com.thoughtworks.legacycode.remove_duplication.Animal;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class Animals {
9 | private static List wild;
10 |
11 | public static List wild() {
12 | if (wild == null){
13 | wild = new ArrayList();
14 | }
15 | return wild;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_constructor/Enclosure.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_constructor;
2 |
3 | import com.thoughtworks.legacycode.remove_duplication.Animal;
4 |
5 | public class Enclosure {
6 | public void add(Animal animal) {
7 |
8 | }
9 |
10 | public boolean contains(Animal animal) {
11 | return false;
12 | }
13 |
14 | public boolean isEmpty() {
15 | return false;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_constructor/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_constructor;
2 |
3 | public class Main {
4 | public static void main(String[] args) {
5 | final ZooManager zooManager = new ZooManager();
6 | zooManager.moveWildAnimalsToEnclosures();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_constructor/Zoo.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_constructor;
2 |
3 | public class Zoo {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_constructor/ZooManager.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_constructor;
2 |
3 | import com.thoughtworks.legacycode.remove_duplication.Animal;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class ZooManager {
9 | private Zoo zoo;
10 | private List enclosures;
11 |
12 | public ZooManager() {
13 | zoo = new Zoo();
14 | }
15 |
16 | public int numberOfEnclosures(){
17 | if (enclosures == null){
18 | enclosures = new ArrayList();
19 | }
20 | return enclosures.size();
21 | }
22 |
23 | public void addEnclosure(Enclosure enclosure){
24 | if (enclosures == null){
25 | enclosures = new ArrayList();
26 | }
27 | enclosures.add(enclosure);
28 | }
29 |
30 | public void moveWildAnimalsToEnclosures(){
31 | for (Animal animal : Animals.wild()) {
32 | findEnclosureFor(animal).add(animal);
33 | }
34 | Animals.wild().clear();
35 | }
36 |
37 | public Zoo getZoo() {
38 | return zoo;
39 | }
40 |
41 | private Enclosure findEnclosureFor(Animal animal) {
42 | if (enclosures == null){
43 | enclosures = new ArrayList();
44 | }
45 | for (Enclosure enclosure : enclosures) {
46 | if (enclosure.contains(animal)){
47 | return enclosure;
48 | }
49 | }
50 | for (Enclosure enclosure : enclosures) {
51 | if (enclosure.isEmpty()){
52 | return enclosure;
53 | }
54 | }
55 | return null;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_method/Book.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_method;
2 |
3 | public class Book {
4 | private boolean overDue=false;
5 | private String name;
6 |
7 | public Book(String name) {
8 |
9 | this.name = name;
10 | }
11 |
12 | public boolean isOverDue() {
13 | return overDue;
14 | }
15 |
16 | public String getName() {
17 | return name;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_method/Library.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_method;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.io.PrintStream;
7 | import java.util.List;
8 |
9 | public class Library {
10 | private PrintStream printStream;
11 |
12 | public Library(PrintStream printStream) {
13 | this.printStream = printStream;
14 | }
15 |
16 | public void printBooks(List books) throws IOException {
17 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
18 | final String libraryName = bufferedReader.readLine();
19 | printStream.println(libraryName);
20 | for (Book book : books) {
21 | printStream.println(book.getName());
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/parameterize_method/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_method;
2 |
3 | import java.io.IOException;
4 | import java.util.List;
5 |
6 | import static java.util.Arrays.asList;
7 |
8 | public class Main {
9 | public static void main(String[] args) throws IOException {
10 | List bookList = asList(new Book("The Two Towers"), new Book("House of Leaves"));
11 | final Library library = new Library(System.out);
12 | library.printBooks(bookList);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/primitivize_parameter/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 | public class Main {
4 | public static void main(String[] args) {
5 | final Monster monster1 = new Monster(new Vector2(0, 0));
6 | final Monster monster2 = new Monster(new Vector2(10, 10));
7 |
8 | MoveToMeetingPointCommand command = new MoveToMeetingPointCommand(monster1, monster2);
9 | command.execute();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/primitivize_parameter/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 | public class Monster {
4 |
5 | private Physics physics = Physics.getInstance();
6 | private Rendering rendering = new Rendering();
7 | private Vector2 position;
8 |
9 |
10 | public Monster(Vector2 position) {
11 | this.position = position;
12 | physics.add(this);
13 | rendering.render(this);
14 | }
15 |
16 | public Vector2 position() {
17 | return position;
18 | }
19 |
20 | public void moveTo(Vector2 destination) {
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/primitivize_parameter/MoveToMeetingPointCommand.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 | public class MoveToMeetingPointCommand {
4 | private final Monster monster1;
5 | private final Monster monster2;
6 |
7 | public MoveToMeetingPointCommand(Monster monster1, Monster monster2) {
8 | this.monster1 = monster1;
9 | this.monster2 = monster2;
10 | }
11 |
12 | public void execute() {
13 | Vector2 goalLocation = findMidpoint(monster1, monster2);
14 | monster1.moveTo(goalLocation);
15 | monster2.moveTo(goalLocation);
16 | }
17 |
18 | private Vector2 findMidpoint(Monster monster1, Monster monster2) {
19 | return monster1.position().average(monster2.position());
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/primitivize_parameter/Physics.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 | public class Physics {
4 | public static Physics getInstance() {
5 | return null;
6 | }
7 |
8 | public void add(Monster monster) {
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/primitivize_parameter/Rendering.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 | public class Rendering {
4 | public void render(Monster monster) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/primitivize_parameter/Vector2.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 |
4 | public class Vector2 {
5 | private double x;
6 | private double y;
7 |
8 | public Vector2(double x, double y) {
9 | this.x = x;
10 | this.y = y;
11 | }
12 |
13 | public Vector2 average(Vector2 that) {
14 | return new Vector2(average(this.x, that.x), average(this.y, that.y));
15 | }
16 |
17 | private double average(double number1, double number2) {
18 | return (number1 + number2)/2;
19 | }
20 |
21 | public Vector2 minus(Vector2 that) {
22 | return null;
23 | }
24 |
25 | public Vector2 add(Vector2 that) {
26 | return null;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/pull_up_feature/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.pull_up_feature;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | Monster monster = new Monster(10, "Angry", new Vector2(0, 0));
8 | monster.processAi();monster.draw();
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/pull_up_feature/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.pull_up_feature;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster {
6 |
7 | private Physics physics = Physics.getInstance();
8 | private Rendering rendering = new Rendering();
9 | private Integer hitPoints;
10 | private String state;
11 | private Vector2 position;
12 | private Integer damage = 2;
13 | // many more instance variables
14 |
15 |
16 | public Monster(Integer hitPoints, String state, Vector2 position) {
17 | this.hitPoints = hitPoints;
18 | this.state = state;
19 | this.position = position;
20 | physics.add(this);
21 | rendering.render(this);
22 | }
23 |
24 | // Pull this up into an abstract base class. Make all other methods abstract.
25 | public void processAi(){
26 | if (hitPoints > 5){
27 | Monster nearestEnemy = findNearestEnemy();
28 | if (state.equals("Angry")){
29 | moveTo(nearestEnemy);
30 | attack(nearestEnemy);
31 | } else if (state.equals("Afraid")){
32 | moveAwayFrom(nearestEnemy);
33 | }
34 | } else {
35 | rest();
36 | }
37 | }
38 |
39 | // Stub this out in new child class so it returns a canned result that you can
40 | public Monster findNearestEnemy() {
41 | return physics.findNearestEntityTo(position);
42 | }
43 |
44 | public void rest() {
45 | hitPoints++;
46 | if (hitPoints > 5){
47 | state = "Angry";
48 | }
49 | }
50 |
51 | public void moveAwayFrom(Monster target) {
52 | Vector2 offsetFromMonsterToMe = position.minus(target.position);
53 | moveTo(position.add(offsetFromMonsterToMe));
54 | }
55 |
56 | public void attack(Monster target) {
57 | target.hitPoints -= damage;
58 | }
59 |
60 | public void moveTo(Monster target) {
61 | moveTo(target.position);
62 | }
63 |
64 | public void draw(){
65 | rendering.render(this);
66 | }
67 |
68 | public void moveTo(Vector2 position){
69 | this.position = position;
70 | physics.move(this, position);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/pull_up_feature/Physics.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.pull_up_feature;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Physics {
6 | public static Physics getInstance() {
7 | return null;
8 | }
9 |
10 | public void add(Monster monster) {
11 |
12 | }
13 |
14 | public Monster findNearestEntityTo(Vector2 position) {
15 | return null;
16 | }
17 |
18 | public void move(Monster entity, Vector2 position) {
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/pull_up_feature/Rendering.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.pull_up_feature;
2 |
3 | public class Rendering {
4 | public void render(Monster monster) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/push_down_dependency/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.push_down_dependency;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | Monster monster = new Monster(10, "Angry", new Vector2(0, 0));
8 | monster.processAi();
9 | monster.draw();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/push_down_dependency/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.push_down_dependency;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster {
6 |
7 | // Push Physics and Rendering down
8 | private Physics physics = Physics.getInstance();
9 | private Rendering rendering = new Rendering();
10 |
11 | private Integer hitPoints;
12 | private String state;
13 | private Vector2 position;
14 | private Integer damage = 2;
15 | // many more instance variables
16 |
17 |
18 | public Monster(Integer hitPoints, String state, Vector2 position) {
19 | this.hitPoints = hitPoints;
20 | this.state = state;
21 | this.position = position;
22 | // Push Physics and Rendering down
23 | physics.add(this);
24 | rendering.render(this);
25 | }
26 |
27 | public void processAi(){
28 | if (hitPoints > 5){
29 | Monster nearestEnemy = findNearestEnemy();
30 | if (state.equals("Angry")){
31 | moveTo(nearestEnemy);
32 | attack(nearestEnemy);
33 | } else if (state.equals("Afraid")){
34 | moveAwayFrom(nearestEnemy);
35 | }
36 | } else {
37 | rest();
38 | }
39 | }
40 |
41 | // Push this down
42 | public Monster findNearestEnemy() {
43 | return physics.findNearestEntityTo(position);
44 | }
45 |
46 | public void rest() {
47 | hitPoints++;
48 | if (hitPoints > 5){
49 | state = "Angry";
50 | }
51 | }
52 |
53 | public void moveAwayFrom(Monster target) {
54 | Vector2 offsetFromMonsterToMe = position.minus(target.position);
55 | moveTo(position.add(offsetFromMonsterToMe));
56 | }
57 |
58 | public void attack(Monster target) {
59 | target.hitPoints -= damage;
60 | }
61 |
62 | public void moveTo(Monster target) {
63 | moveTo(target.position);
64 | }
65 |
66 | // Push this down
67 | public void draw(){
68 | rendering.render(this);
69 | }
70 |
71 | // Push this down
72 | public void moveTo(Vector2 position){
73 | this.position = position;
74 | physics.move(this, position);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/push_down_dependency/Physics.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.push_down_dependency;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Physics {
6 | public static Physics getInstance() {
7 | return null;
8 | }
9 |
10 | public void add(Monster monster) {
11 |
12 | }
13 |
14 | public Monster findNearestEntityTo(Vector2 position) {
15 | return null;
16 | }
17 |
18 | public void move(Monster entity, Vector2 position) {
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/push_down_dependency/Rendering.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.push_down_dependency;
2 |
3 | public class Rendering {
4 | public void render(Monster monster) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/remove_duplication/Animal.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.remove_duplication;
2 |
3 | public interface Animal {
4 | boolean likes(String thing);
5 |
6 | String name();
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/remove_duplication/Cat.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.remove_duplication;
2 |
3 | public class Cat implements Animal{
4 | @Override
5 | public boolean likes(String thing) {
6 | return false;
7 | }
8 |
9 | @Override
10 | public String name() {
11 | return null;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/remove_duplication/Dog.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.remove_duplication;
2 |
3 | public class Dog implements Animal {
4 | @Override
5 | public boolean likes(String thing) {
6 | return false;
7 | }
8 |
9 | @Override
10 | public String name() {
11 | return null;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/remove_duplication/Duplication.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.remove_duplication;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 |
6 | public class Duplication {
7 | public static void main(String[] args) {
8 | ArrayList animals = new ArrayList();
9 | Animal cat = new Cat();
10 | Animal dog = new Dog();
11 | Animal fish = new Fish();
12 | animals.addAll(Arrays.asList(cat, dog, fish));
13 |
14 | Integer command = 9;
15 | switch(command){
16 | case 1:
17 | for (Animal animal : animals) {
18 | if (animal.likes("water")){
19 | System.out.println(animal.name() + "likes water.");
20 | }
21 | }
22 | break;
23 | case 2:
24 | for (Animal animal : animals) {
25 | if (animal.likes("running")){
26 | System.out.println(animal.name() + "likes running.");
27 | }
28 | }
29 | break;
30 | case 3:
31 | int indexOfCat = animals.lastIndexOf(cat);
32 | animals.add(indexOfCat, new Cat());
33 | break;
34 | case 4:
35 | animals.add(animals.lastIndexOf(dog), new Dog());
36 | break;
37 | case 5:
38 | animals.add(animals.lastIndexOf(fish), new Fish());
39 | break;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/remove_duplication/Fish.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.remove_duplication;
2 |
3 | public class Fish implements Animal{
4 | @Override
5 | public boolean likes(String thing) {
6 | return false;
7 | }
8 |
9 | @Override
10 | public String name() {
11 | return null;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/spawn_class/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.spawn_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | Monster monster = new Monster(10, "Angry", new Vector2(0, 0));
8 | monster.processAi();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/spawn_class/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.spawn_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster {
6 |
7 | private Integer hitPoints;
8 | private String state;
9 | private Vector2 position;
10 | private Integer damage = 2;
11 |
12 |
13 | public Monster(Integer hitPoints, String state, Vector2 position) {
14 | this.hitPoints = hitPoints;
15 | this.state = state;
16 | this.position = position;
17 | }
18 |
19 | public void processAi(){
20 | // Create an instance of a new class here and call a method on it.
21 | if (hitPoints > 5){
22 | String enemyName = System.console().readLine();
23 | Monster enemy = findEnemyByName(enemyName);
24 | if (state.equals("Angry")){
25 | moveTo(enemy);
26 | attack(enemy);
27 | } else if (state.equals("Afraid")){
28 | moveAwayFrom(enemy);
29 | }
30 | } else {
31 | rest();
32 | }
33 | }
34 |
35 | private Monster findEnemyByName(String enemyName) {
36 | return null;
37 | }
38 |
39 | public void rest() {
40 | hitPoints++;
41 | if (hitPoints > 5){
42 | state = "Angry";
43 | }
44 | }
45 |
46 | public void moveAwayFrom(Monster target) {
47 | Vector2 offsetFromMonsterToMe = position.minus(target.position);
48 | moveTo(position.add(offsetFromMonsterToMe));
49 | }
50 |
51 | public void attack(Monster target) {
52 | target.hitPoints -= damage;
53 | }
54 |
55 | public void moveTo(Monster target) {
56 | moveTo(target.position);
57 | }
58 |
59 | public void moveTo(Vector2 position){
60 | this.position = position;
61 | }
62 |
63 | public String state() {
64 | return state;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/spawn_method/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.spawn_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | new Monster(10, "Angry", new Vector2(0, 0)).processAi();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/spawn_method/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.spawn_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster {
6 |
7 | private Integer hitPoints;
8 | private String state;
9 | private Vector2 position;
10 | private Integer damage = 2;
11 |
12 |
13 | public Monster(Integer hitPoints, String state, Vector2 position) {
14 | this.hitPoints = hitPoints;
15 | this.state = state;
16 | this.position = position;
17 | }
18 |
19 | public void processAi(){
20 | // Call new method here. It should be public so you can test it.
21 | if (hitPoints > 5){
22 | String enemyName = System.console().readLine();
23 | Monster enemy = findEnemyByName(enemyName);
24 | if (state.equals("Angry")){
25 | moveTo(enemy);
26 | attack(enemy);
27 | } else if (state.equals("Afraid")){
28 | moveAwayFrom(enemy);
29 | }
30 | } else {
31 | rest();
32 | }
33 | }
34 |
35 | public Monster findEnemyByName(String enemyName) {
36 | return null;
37 | }
38 |
39 | public void rest() {
40 | hitPoints++;
41 | if (hitPoints > 5){
42 | state = "Angry";
43 | }
44 | }
45 |
46 | public void moveAwayFrom(Monster target) {
47 | Vector2 offsetFromMonsterToMe = position.minus(target.position);
48 | moveTo(position.add(offsetFromMonsterToMe));
49 | }
50 |
51 | public void attack(Monster target) {
52 | target.hitPoints -= damage;
53 | }
54 |
55 | public void moveTo(Monster target) {
56 | moveTo(target.position);
57 | }
58 |
59 | public void moveTo(Vector2 position){
60 | this.position = position;
61 | }
62 |
63 | public String state() {
64 | return state;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/subclass_and_override_method/AiEntity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public abstract class AiEntity {
6 |
7 | private Integer hitPoints;
8 | private String state;
9 | protected Vector2 position;
10 | private Integer damage = 2;
11 | // many more instance variables
12 |
13 |
14 | public AiEntity(Integer hitPoints, String state, Vector2 position) {
15 | this.hitPoints = hitPoints;
16 | this.state = state;
17 | this.position = position;
18 | }
19 |
20 | public void processAi(){
21 | if (hitPoints > 5){
22 | AiEntity nearestEnemy = findNearestEnemy();
23 | if (state.equals("Angry")){
24 | moveTo(nearestEnemy);
25 | attack(nearestEnemy);
26 | } else if (state.equals("Afraid")){
27 | moveAwayFrom(nearestEnemy);
28 | }
29 | } else {
30 | rest();
31 | }
32 | }
33 |
34 | // Override this method in new subclass because Physics is scary
35 | // We can also stub this to return the enemy that we will use for our assertion
36 | public abstract AiEntity findNearestEnemy();
37 |
38 | public void rest() {
39 | hitPoints++;
40 | if (hitPoints > 5){
41 | state = "Angry";
42 | }
43 | }
44 |
45 | public void moveAwayFrom(AiEntity target) {
46 | Vector2 offsetFromMonsterToMe = position.minus(target.position);
47 | moveTo(position.add(offsetFromMonsterToMe));
48 | }
49 |
50 | public void attack(AiEntity target) {
51 | target.hitPoints -= damage;
52 | }
53 |
54 | public abstract void moveTo(AiEntity target);
55 |
56 | public abstract void draw();
57 |
58 | // Override this method in new subclass because Physics is scary
59 | public abstract void moveTo(Vector2 position);
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/subclass_and_override_method/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | // AiEntity aiEntity = new Monster(10, "Angry", new Vector2(0, 0));
8 | // aiEntity.processAi();
9 | // aiEntity.draw();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/subclass_and_override_method/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster extends AiEntity {
6 | private Physics physics;
7 | private Rendering rendering;
8 |
9 | public Monster(Physics physics, Rendering rendering, Integer hitPoints, String state, Vector2 position) {
10 | super(hitPoints, state, position);
11 | this.physics = physics;
12 | this.rendering = rendering;
13 | this.physics.add(this);
14 | this.rendering.render(this);
15 | }
16 |
17 | public void moveTo(Vector2 position){
18 | this.position = position;
19 | physics.move(this, position);
20 | }
21 |
22 | public void draw(){
23 | rendering.render(this);
24 | }
25 |
26 | public AiEntity findNearestEnemy() {
27 | return physics.findNearestEntityTo(position);
28 | }
29 |
30 | public void moveTo(AiEntity target) {
31 | moveTo(target.position);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/subclass_and_override_method/Physics.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Physics {
6 | public static Physics getInstance() {
7 | return null;
8 | }
9 |
10 | public void add(AiEntity aiEntity) {
11 |
12 | }
13 |
14 | public AiEntity findNearestEntityTo(Vector2 position) {
15 | return null;
16 | }
17 |
18 | public void move(AiEntity entity, Vector2 position) {
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/subclass_and_override_method/Rendering.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | public class Rendering {
4 | public void render(AiEntity aiEntity) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/subclass_and_override_method/TestMonster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class TestMonster extends AiEntity {
6 | public AiEntity nearestEnemy;
7 | public AiEntity enemyWeMovedTo;
8 |
9 | public TestMonster(Integer hitPoints, String state, Vector2 position) {
10 | super(hitPoints, state, position);
11 | }
12 |
13 | @Override
14 | public AiEntity findNearestEnemy() {
15 | return nearestEnemy;
16 | }
17 |
18 | @Override
19 | public void moveTo(AiEntity target) {
20 | enemyWeMovedTo = target;
21 | }
22 |
23 | @Override
24 | public void draw() {
25 |
26 | }
27 |
28 | @Override
29 | public void moveTo(Vector2 position) {
30 |
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_class/GameEntity.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public interface GameEntity {
6 | void processAi();
7 |
8 | void moveTo(GameEntity target);
9 |
10 | void draw();
11 |
12 | void moveTo(Vector2 position);
13 |
14 | Vector2 position();
15 |
16 | void decreaseHitPoints(Integer damage);
17 |
18 | String state();
19 |
20 | void position(Vector2 position);
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_class/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | GameEntity monster = new Monster(10, "Angry", new Vector2(0, 0));
8 | monster.processAi();
9 | monster.draw();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_class/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster implements GameEntity{
6 |
7 | private Physics physics = Physics.getInstance();
8 | private Rendering rendering = new Rendering();
9 | private Integer hitPoints;
10 | private String state;
11 | private Vector2 position;
12 | private Integer damage = 2;
13 | // many more instance variables
14 |
15 |
16 | public Monster(Integer hitPoints, String state, Vector2 position) {
17 | this.hitPoints = hitPoints;
18 | this.state = state;
19 | this.position = position;
20 | physics.add(this);
21 | rendering.render(this);
22 | }
23 |
24 | @Override
25 | public void processAi(){
26 | if (hitPoints > 5){
27 | String nameOfEnemy = System.console().readLine();
28 | GameEntity enemy = findEnemyByName(nameOfEnemy);
29 | if (state.equals("Angry")){
30 | moveTo(enemy);
31 | attack(enemy);
32 | } else if (state.equals("Afraid")){
33 | moveAwayFrom(enemy);
34 | }
35 | } else {
36 | rest();
37 | }
38 | }
39 |
40 | private GameEntity findEnemyByName(String nameOfEnemy) {
41 | return null;
42 | }
43 |
44 | public void rest() {
45 | hitPoints++;
46 | if (hitPoints > 5){
47 | state = "Angry";
48 | }
49 | }
50 |
51 | public void moveAwayFrom(GameEntity target) {
52 | Vector2 offsetFromMonsterToMe = position.minus(target.position());
53 | moveTo(position.add(offsetFromMonsterToMe));
54 | }
55 |
56 | public void attack(GameEntity target) {
57 | target.decreaseHitPoints(damage);
58 | }
59 |
60 | @Override
61 | public void moveTo(GameEntity target) {
62 | moveTo(target.position());
63 | }
64 |
65 | @Override
66 | public void draw(){
67 | rendering.render(this);
68 | }
69 |
70 | @Override
71 | public void moveTo(Vector2 position){
72 | this.position = position;
73 | physics.move(this, position);
74 | }
75 |
76 | @Override
77 | public Vector2 position() {
78 | return position;
79 | }
80 |
81 | @Override
82 | public void decreaseHitPoints(Integer damage) {
83 | hitPoints -= damage;
84 | }
85 |
86 | @Override
87 | public String state() {
88 | return state;
89 | }
90 |
91 | @Override
92 | public void position(Vector2 position) {
93 | this.position = position;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_class/Physics.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Physics {
6 | private Monster monster;
7 |
8 | public static Physics getInstance() {
9 | return null;
10 | }
11 |
12 | public void add(Monster monster) {
13 | this.monster = monster;
14 | }
15 |
16 | public void move(Monster entity, Vector2 position) {
17 | entity.position(position);
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_class/Rendering.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_class;
2 |
3 | public class Rendering {
4 | public void render(Monster monster) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_method/Main.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | Monster monster = new Monster(10, "Angry", new Vector2(0, 0));
8 | monster.processAi();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/thoughtworks/legacycode/wrap_method/Monster.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 |
5 | public class Monster {
6 | private Integer hitPoints;
7 | private String state;
8 | private Vector2 position;
9 | private Integer damage = 2;
10 |
11 |
12 | public Monster(Integer hitPoints, String state, Vector2 position) {
13 | this.hitPoints = hitPoints;
14 | this.state = state;
15 | this.position = position;
16 | }
17 |
18 | // Wrap this in a new method that call the method you want to test. Don't test this new method
19 | public void processAi(){
20 | if (hitPoints > 5){
21 | Monster nearestEnemy = findNearestEnemy();
22 | if (state.equals("Angry")){
23 | moveTo(nearestEnemy);
24 | attack(nearestEnemy);
25 | } else if (state.equals("Afraid")){
26 | moveAwayFrom(nearestEnemy);
27 | }
28 | } else {
29 | rest();
30 | }
31 | }
32 |
33 | public Monster findNearestEnemy() {
34 | return null;
35 | }
36 |
37 | public void rest() {
38 | hitPoints++;
39 | if (hitPoints > 5){
40 | state = "Angry";
41 | }
42 | }
43 |
44 | public void moveAwayFrom(Monster target) {
45 | Vector2 offsetFromMonsterToMe = position.minus(target.position);
46 | moveTo(position.add(offsetFromMonsterToMe));
47 | }
48 |
49 | public void attack(Monster target) {
50 | target.hitPoints -= damage;
51 | }
52 |
53 | public void moveTo(Monster target) {
54 | moveTo(target.position);
55 | }
56 |
57 | public void moveTo(Vector2 position){
58 | this.position = position;
59 | }
60 |
61 | public String state() {
62 | return state;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/adapt_parameter/ReportGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.adapt_parameter;
2 |
3 | import org.junit.Test;
4 |
5 | public class ReportGeneratorTest {
6 |
7 | // Adapt Parameter
8 |
9 | // Use adapter pattern on tough dependency
10 | //
11 | // Wrap the console parameter in a new class that we can then mock
12 |
13 | @Test
14 | public void shouldPrintConsoleInputSomething() {
15 | ReportGenerator reportGenerator = new ReportGenerator();
16 |
17 | // Change
18 | reportGenerator.reportToConsole(System.console());
19 |
20 | // Verify that we printed the string that the user entered into the console
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/break_out_method_object/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.break_out_method_object;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.mockito.Mockito.mock;
6 | import static org.mockito.Mockito.verify;
7 | import static org.mockito.Mockito.when;
8 |
9 | // Break out Method Object
10 |
11 | // Extract method you want to change into a new class and test that
12 | // There is a hint in the Monster class
13 |
14 | public class AiEntityTest {
15 | @Test
16 | public void shouldBecomeDeadWhenAtZeroOrFewerHitPoints() {
17 | Monster monster = mock(Monster.class);
18 | when(monster.hitPoints()).thenReturn(0);
19 |
20 | // Call method in new method object here
21 |
22 | verify(monster).state("");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/encapsulate_global_reference/LibraryTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.encapsulate_global_reference;
2 |
3 | import org.junit.Test;
4 |
5 | public class LibraryTest {
6 | @Test
7 | public void shouldDoSomething() {
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/expose_static_method/LibraryTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.expose_static_method;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import static org.hamcrest.CoreMatchers.hasItem;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class LibraryTest {
12 | // Expose Static Method
13 |
14 | // Change existing method to be static (if it can be). You can test without an instance
15 |
16 | @Test
17 | public void shouldFindOverdueBooks() {
18 | List books = new ArrayList();
19 | Book book = new Book("Starship Troopers", true);
20 | books.add(book);
21 |
22 | // Test will pause here while waiting for user input
23 | Library library = new Library(System.console());
24 |
25 | // If overdueBooks was static we wouldn't have to create a new library
26 | List overdueBooks = library.overdueBooks(books);
27 |
28 | assertThat(overdueBooks, hasItem(book));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/extract_and_override_call/LibraryTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.extract_and_override_call;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.PrintStream;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import static org.mockito.Mockito.mock;
10 | import static org.mockito.Mockito.verify;
11 |
12 | public class LibraryTest {
13 |
14 | // Extract and Override Call
15 |
16 | // Extract tough dependency and override it then test child class
17 |
18 | @Test
19 | public void shouldPrintNameOfBookWhenThereIsOneBook() {
20 | List books = new ArrayList();
21 | books.add(new Book("Ringworld"));
22 | PrintStream printStream = mock(PrintStream.class);
23 |
24 | // Create instance of a new subclass of Library and call printBooks
25 |
26 | verify(printStream).println("Ringworld");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/parameterize_constructor/ZooManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_constructor;
2 |
3 | import org.junit.Test;
4 |
5 | public class ZooManagerTest {
6 | // Parameterize Constructor
7 | // Inject a dependency instead of leaving it internal to a class
8 | //
9 | // Break the dependencies on Animals.wild() and internally initialized enclosures so you can set them up properly for
10 | // this test case and verify the correct results
11 |
12 |
13 | @Test
14 | public void shouldPlaceAnimalInTheSameCageAsAlreadyCagedAnimalOfTheSameType() {
15 | ZooManager zooManager = new ZooManager();
16 | zooManager.moveWildAnimalsToEnclosures();
17 | // assert or verify that the new animal ended up in the correct enclosure
18 | }
19 |
20 | @Test
21 | public void shouldPlaceAllWildAnimals() {
22 | ZooManager zooManager = new ZooManager();
23 | zooManager.moveWildAnimalsToEnclosures();
24 | // assert or verify that there are no wild animals left
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/parameterize_method/LibraryTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.parameterize_method;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 | import java.io.PrintStream;
7 | import java.security.spec.PSSParameterSpec;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import static org.mockito.Mockito.mock;
12 | import static org.mockito.Mockito.verify;
13 |
14 | public class LibraryTest {
15 | // Parameterize Method
16 | // Inject a dependency instead of leaving it internal to a method
17 | //
18 | // Break the dependency on BufferedReader by injecting it into the method
19 |
20 | @Test
21 | public void shouldPrintTheBookNameWhenThereIsOneBook() throws IOException {
22 | PrintStream printStream = mock(PrintStream.class);
23 | Library library = new Library(printStream);
24 | List books = new ArrayList();
25 | books.add(new Book("House of Leaves"));
26 |
27 | library.printBooks(books);
28 |
29 | verify(printStream).println("House of Leaves");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/primitivize_parameter/MoveToMeetingPointCommandTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.primitivize_parameter;
2 |
3 | import org.junit.Test;
4 |
5 | public class MoveToMeetingPointCommandTest {
6 | @Test
7 | public void shouldDoSomething() {
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/pull_up_feature/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.pull_up_feature;
2 |
3 | import org.junit.Test;
4 |
5 | public class AiEntityTest {
6 |
7 | // Pull up Feature
8 | //
9 | // Pull the parts of a class you want to test into a new abstract base class then test a child of that
10 |
11 | @Test
12 | public void shouldMoveToClosestEnemyWhenHealthyAndAngry() {
13 | // Integer hitPoint = 10;
14 | // String state = "Angry";
15 |
16 | // Create instance of new child class here
17 |
18 | // use stubs in test class to make sure the right interaction happen
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/push_down_dependency/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.push_down_dependency;
2 |
3 | import org.junit.Test;
4 |
5 | public class AiEntityTest {
6 | // Push down Dependencies
7 |
8 | // Make current class abstract and push your dependencies into a child class. Test through a new test child class.
9 |
10 | @Test
11 | public void shouldMoveToClosestEnemyWhenHealthyAndAngry() {
12 | // Integer hitPoints = 10;
13 | // String state = "Angry";
14 |
15 | // Create instance of child class with stubbed out dependencies
16 |
17 | // check that moveTo was called with the Monster returned from findNearestEnemy
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/spawn_class/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.spawn_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.core.Is.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 |
10 | // Spawn Class
11 | //
12 | // Introduce a new class for the test below and TDD a method on that class. Don't test that the new method is called by processAi
13 | // There is a hint in the Monster class
14 |
15 | public class AiEntityTest {
16 | @Test
17 | public void shouldBecomeDeadWhenAtZeroOrFewerHitPoints() {
18 | int hitPoints = 0;
19 | Monster monster = new Monster(hitPoints, "Angry", new Vector2(0, 0));
20 | // Call new method here
21 |
22 | assertThat(monster.state(), is("Dead"));
23 | }
24 | }
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/spawn_method/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.spawn_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.core.Is.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 | // Spawn Method
10 | //
11 | // Introduce a method for the test below and TDD that method. Don't test that the new method is called by processAi
12 | // There is a hint in the Monster class
13 |
14 | public class AiEntityTest {
15 | @Test
16 | public void shouldBecomeDeadWhenAtZeroOrFewerHitPoints() {
17 | int hitPoints = 0;
18 | Monster monster = new Monster(hitPoints, "Angry", new Vector2(0, 0));
19 | // Call new method here
20 |
21 | assertThat(monster.state(), is("Dead"));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/subclass_and_override_method/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.subclass_and_override_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class AiEntityTest {
10 |
11 | // Subclass and Override Method
12 |
13 | // Test a subclass of your real class and override methods with dependencies
14 |
15 | @Test
16 | public void shouldMoveToClosestEnemyWhenHealthy() {
17 |
18 | TestMonster monster = new TestMonster(10, "Angry", new Vector2(0, 0));
19 | monster.nearestEnemy = new TestMonster(10, "", new Vector2(1,1));
20 | monster.processAi();
21 |
22 | assertThat(monster.enemyWeMovedTo, is(monster.nearestEnemy));
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/wrap_class/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_class;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.core.Is.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class AiEntityTest {
10 | // Wrap Class aka Decorator
11 |
12 | // Wrap your hard to test class with a Decorator and TDD the decorator
13 | //
14 | // Create a new class that implements the GameEntity interface and also 'has-a' GameEntity
15 |
16 | @Test
17 | public void shouldBecomeDeadWhenAtZeroOrFewerHitPoints() {
18 | int hitPoints = 0;
19 | // Don't create an actual Monster here. Inject a mock GameEntity into your new decorator class
20 | GameEntity gameEntity = new Monster(hitPoints, "Angry", new Vector2(0, 0));
21 |
22 | gameEntity.processAi();
23 |
24 | assertThat(gameEntity.state(), is("Dead"));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/com/thoughtworks/legacycode/wrap_method/AiEntityTest.java:
--------------------------------------------------------------------------------
1 | package com.thoughtworks.legacycode.wrap_method;
2 |
3 | import com.thoughtworks.legacycode.primitivize_parameter.Vector2;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.core.Is.is;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class AiEntityTest {
10 | //Wrap Method
11 | // New method (wraps the existing method and the method that will contain our new behavior) * Don't test
12 | // |------- Existing Method (processAi) * Don't test
13 | // |------- Method we want to TDD (this is where we make the monster dead if it has zero or fewer hitPoints
14 | // Introduce a method that contains an existing method and a call to you new method
15 |
16 | @Test
17 | public void shouldBecomeDeadWhenFewerThan1HitPoints() {
18 | Integer hitPoints = 0;
19 | Monster monster = new Monster(hitPoints, "Angry", new Vector2(0, 0));
20 |
21 | // Call new method instead of processAi
22 | monster.processAi();
23 |
24 | assertThat(monster.state(), is("DEAD"));
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------