├── .gitattributes
├── .gitignore
├── LICENSE
├── pom.xml
└── src
├── main
└── java
│ ├── cleancode
│ ├── Address.java
│ ├── BouleanParameters.java
│ ├── ManyParamsOOP.java
│ ├── ManyParamsVO.java
│ └── pretend
│ │ ├── Autowired.java
│ │ ├── Inject.java
│ │ └── Service.java
│ └── videostore
│ └── horror
│ ├── Customer.java
│ ├── Movie.java
│ ├── Rental.java
│ └── StatementFormatter.java
└── test
└── java
└── videostore
└── horror
└── StatementFormatterTest.java
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | .idea
26 | *.iml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Victor Rentea
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | victor.training
5 | clean-code-java
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 |
10 | junit
11 | junit
12 | 4.12
13 | test
14 |
15 |
16 | javax.inject
17 | javax.inject
18 | 1
19 |
20 |
21 | org.projectlombok
22 | lombok
23 | 1.18.10
24 |
25 |
26 |
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-compiler-plugin
31 | 3.8.1
32 |
33 | 1.8
34 | 1.8
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/main/java/cleancode/Address.java:
--------------------------------------------------------------------------------
1 | package cleancode;
2 |
3 | public class Address {
4 | private final String city;
5 | private final String streetName;
6 | private final Integer streetNumber;
7 |
8 | public Address(String city, String streetName, Integer streetNumber) {
9 | this.city = city;
10 | this.streetName = streetName;
11 | this.streetNumber = streetNumber;
12 | }
13 |
14 | public String getCity() {
15 | return city;
16 | }
17 |
18 | public String getStreetName() {
19 | return streetName;
20 | }
21 |
22 | public Integer getStreetNumber() {
23 | return streetNumber;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cleancode/BouleanParameters.java:
--------------------------------------------------------------------------------
1 | package cleancode;
2 |
3 | import java.util.List;
4 |
5 | @SuppressWarnings("all")
6 | public class BouleanParameters {
7 |
8 | public static void main(String[] args) {
9 | // The method is called from various foreign places in the codebase
10 | bigUglyMethod(1, 2);
11 | bigUglyMethod(1, 2);
12 | bigUglyMethod(1, 2);
13 | bigUglyMethod(1, 2);
14 | bigUglyMethod(1, 2);
15 |
16 | // TODO From my use-case 323, I call it too, to do more within:
17 | bigUglyMethod323(1, 2);
18 |
19 | }
20 |
21 | static void bigUglyMethod(int a, int b) {
22 | pre();
23 | post();
24 | }
25 | static void bigUglyMethod323(int a, int b) {
26 | pre();
27 | System.out.println("This too for US323");
28 | post();
29 | }
30 |
31 | private static void post() {
32 | System.out.println("More Complex Logic");
33 | System.out.println("More Complex Logic");
34 | System.out.println("More Complex Logic");
35 | }
36 |
37 | private static void pre() {
38 | System.out.println("Complex Logic");
39 | System.out.println("Complex Logic");
40 | System.out.println("Complex Logic");
41 | }
42 |
43 | // ============== "BOSS" LEVEL: Deeply nested. A lot harder to break down =================
44 |
45 | static int bossLevelStuffFluff323(List tasks) {
46 | int i = 0;
47 | int j = 1;
48 | System.out.println("Logic1");
49 | System.out.println("Logic2");
50 | if (tasks == null) {
51 | return -1;
52 | }
53 | System.out.println("Logic3");
54 | for (int task : tasks) {
55 | i += task * i;
56 | System.out.println("Logic4 " + task);
57 | // TODO HERE, when call this method, I want MY own custom code to run here
58 | System.out.println("Logic5 " + i);
59 | }
60 | System.out.println("Logic6 " + j++);
61 | return i;
62 | }
63 | static int bossLevelStuffFluff(List tasks) {
64 | int i = 0;
65 | int j = 1;
66 | System.out.println("Logic1");
67 | System.out.println("Logic2");
68 | if (tasks == null) {
69 | return -1;
70 | }
71 | System.out.println("Logic3");
72 | for (int task : tasks) {
73 | i += task * i;
74 | System.out.println("Logic4 " + task);
75 | System.out.println("Logic5 " + i);
76 | }
77 | System.out.println("Logic6 " + j++);
78 | return i;
79 | }
80 | static int bossLevelStuffNoFluff(List tasks) {
81 | int i = 0;
82 | int j = 1;
83 | System.out.println("Logic1");
84 | System.out.println("Logic2");
85 | System.out.println("Logic7");
86 | return i;
87 | }
88 | static int bossLevelNoStuff(boolean fluff, List tasks) {
89 | int i = 0;
90 | int j = 1;
91 | System.out.println("Logic1");
92 | System.out.println("Logic7");
93 | return i;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/cleancode/ManyParamsOOP.java:
--------------------------------------------------------------------------------
1 | package cleancode;
2 |
3 | import cleancode.pretend.Service;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | @Service
9 | public class ManyParamsOOP {
10 | private final OtherDependency dep;
11 |
12 | public ManyParamsOOP(OtherDependency dep) {
13 | this.dep = dep;
14 | }
15 |
16 | public void bizLogic() {
17 | Validator validator = new Validator(dep);
18 | validator.m1("a",1);
19 | validator.m2("b",1);
20 | validator.m3("file.txt", 1L,"ref");
21 | validator.m4("a", 1L,5L, "g");
22 | validator.m5(1);
23 | if (!validator.getErrors().isEmpty()) {
24 | throw new IllegalArgumentException(validator.getErrors().toString());
25 | }
26 | }
27 | }
28 | class Validator {
29 | private final OtherDependency dep;
30 |
31 | private List errors = new ArrayList<>();
32 |
33 | public Validator(OtherDependency dep) {
34 | this.dep = dep;
35 | }
36 |
37 | public List getErrors() {
38 | return errors;
39 | }
40 |
41 | public void m1(String a, int b) {
42 | if (a == null) {
43 | errors.add("a must not be null");
44 | }
45 | // stuff
46 | }
47 | public void m2(String s, int c) {
48 | if (c < 0) {
49 | errors.add("negative c");
50 | }
51 | // stuff
52 | }
53 | public void m3(String fileName, long versionId, String reference) {
54 | // stuff
55 | }
56 | public void m4(String a, long listId, long recordId, String g) {
57 | // stuff
58 | }
59 | public void m5(int b) {
60 | // stuff
61 | }
62 | }
63 |
64 | @Service
65 | class OtherDependency {
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/cleancode/ManyParamsVO.java:
--------------------------------------------------------------------------------
1 | package cleancode;
2 |
3 | //@Embeddable
4 | class FullName {
5 | private final String firstName;
6 | private final String lastName;
7 |
8 | public FullName(String firstName, String lastName) {
9 | if (firstName == null || lastName == null) throw new IllegalArgumentException();
10 | this.firstName = firstName;
11 | this.lastName = lastName;
12 | }
13 |
14 | public String getFirstName() {
15 | return firstName;
16 | }
17 |
18 | public String getLastName() {
19 | return lastName;
20 | }
21 |
22 | public String concatenated() {
23 | return firstName + " " + lastName.toUpperCase();
24 | }
25 | }
26 |
27 |
28 | public class ManyParamsVO {
29 | public static void main(String[] args) {
30 | Address address = new Address("St. Albergue", "Paris", 99);
31 | FullName fullName = new FullName("John", "Doe");
32 | new ManyParamsVO().placeOrder(fullName, address);
33 | }
34 |
35 | public void placeOrder(FullName fullName, Address address) {
36 | if (fullName == null) throw new IllegalArgumentException();
37 |
38 | System.out.println("Some Logic for " +
39 | fullName.getFirstName() + " " + fullName.getLastName().toUpperCase());
40 | }
41 | }
42 |
43 | class AnotherClass {
44 | public void otherMethod(FullName fullName, int x) {
45 | if (fullName == null) throw new IllegalArgumentException();
46 |
47 | System.out.println("Another distant Logic");
48 | }
49 | }
50 |
51 |
52 | // over the hills and far away ...
53 | //HOLY ENTITY
54 | class Person {
55 | private FullName fullName;
56 |
57 | public Person(FullName fullName) {
58 | if (fullName == null) throw new IllegalArgumentException();
59 | // TODO think: is this sufficient enforcing ?
60 | }
61 |
62 | public FullName getFullName() {
63 | return fullName;
64 | }
65 |
66 | }
67 |
68 | class PersonService {
69 | public void f(Person person) {
70 | String fullName = person.getFullName().concatenated();
71 | String fullName2 = person.getFullName().concatenated();
72 | String fullName3 = person.getFullName().concatenated();
73 | String fullName4 = person.getFullName().concatenated();
74 | String fullName5 = person.getFullName().concatenated();
75 | System.out.println(fullName);
76 | }
77 | }
--------------------------------------------------------------------------------
/src/main/java/cleancode/pretend/Autowired.java:
--------------------------------------------------------------------------------
1 | package cleancode.pretend;
2 |
3 | public @interface Autowired {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/cleancode/pretend/Inject.java:
--------------------------------------------------------------------------------
1 | package cleancode.pretend;
2 |
3 | public @interface Inject {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/cleancode/pretend/Service.java:
--------------------------------------------------------------------------------
1 | package cleancode.pretend;
2 |
3 | public @interface Service {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/videostore/horror/Customer.java:
--------------------------------------------------------------------------------
1 | package videostore.horror;
2 | // coding kata derived from the Video Store example in Refactoring (1 ed) by Martin Fowler
3 |
4 | import java.util.*;
5 |
6 | class Customer {
7 | private final String name;
8 | private List rentals = new ArrayList<>(); // preserves order
9 |
10 | public Customer(String name) {
11 | this.name = name;
12 | }
13 |
14 | public void addRental(Rental rental) {
15 | rentals.add(rental);
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/src/main/java/videostore/horror/Movie.java:
--------------------------------------------------------------------------------
1 | package videostore.horror;
2 | public class Movie {
3 |
4 | enum Type {
5 | CHILDRENS, REGULAR, NEW_RELEASE;
6 | }
7 | private final String title;
8 | private final Type type;
9 |
10 | public Movie(String title, Type type) {
11 | this.title = title;
12 | this.type = type;
13 | }
14 |
15 | public Type getType() {
16 | return type;
17 | }
18 |
19 | public String getTitle() {
20 | return title;
21 | };
22 | }
--------------------------------------------------------------------------------
/src/main/java/videostore/horror/Rental.java:
--------------------------------------------------------------------------------
1 | package videostore.horror;
2 |
3 | import java.util.Map;
4 | import java.util.function.Function;
5 |
6 | public class Rental {
7 | private final Movie movie;
8 | private final int daysRented;
9 |
10 | public Rental(Movie movie, int daysRented) {
11 | if (daysRented <= 0) throw new IllegalArgumentException("Negative days rented");
12 | this.movie = movie;
13 | this.daysRented = daysRented;
14 | }
15 |
16 | public Movie getMovie() {
17 | return movie;
18 | }
19 |
20 | public int calculateRenterPoints() {
21 | int frequentRenterPoints = 1;
22 | boolean isNewRelease = movie.getType() == Movie.Type.NEW_RELEASE;
23 | if (isNewRelease && daysRented >= 2) {
24 | frequentRenterPoints++;
25 | }
26 | return frequentRenterPoints;
27 | }
28 |
29 | // public double calculatePrice() {
30 | //// return movie.getType().computePrice(daysRented);
31 | // switch (movie.getType()) {
32 | // case REGULAR: return calculateRegularPrice();
33 | // case NEW_RELEASE: return calculateNewReleasePrice();
34 | // case CHILDRENS: return calculateChildrenPrice();
35 | // default:
36 | // throw new IllegalStateException("JDD: Unexpected value: " + movie.getType());
37 | // }
38 | //
39 | // }
40 | // interface PriceCalculator {
41 | // public double calculatePrice();
42 | // }
43 | //// Map> m;
44 | public double calculatePrice() {
45 | // m.get()
46 | // return movie.getType().computePrice(daysRented);
47 | switch (movie.getType()) {
48 | case REGULAR: return calculateRegularPrice();
49 | case NEW_RELEASE: return calculateNewReleasePrice();
50 | case CHILDRENS: return calculateChildrenPrice();
51 | default:
52 | throw new IllegalStateException("JDD: Unexpected value: " + movie.getType());
53 | }
54 |
55 | }
56 |
57 | private double calculateChildrenPrice() {
58 | double price = 1.5;
59 | if (daysRented > 3) {
60 | price += (daysRented - 3) * 1.5;
61 | }
62 | return price;
63 | }
64 |
65 | private int calculateNewReleasePrice() {
66 | return daysRented * 3;
67 | }
68 |
69 | private double calculateRegularPrice() {
70 | double price = 2;
71 | if (daysRented > 2) {
72 | price += (daysRented - 2) * 1.5;
73 | }
74 | return price;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/videostore/horror/StatementFormatter.java:
--------------------------------------------------------------------------------
1 | package videostore.horror;
2 |
3 | import java.util.List;
4 | import java.util.stream.Collectors;
5 |
6 | import static java.util.stream.Collectors.joining;
7 |
8 | public class StatementFormatter {
9 |
10 | public String formatStatement(String customerName, List rentals) {
11 | return formatHeader(customerName) +
12 | formatBody(rentals) +
13 | formatFooter(rentals);
14 | }
15 |
16 | private String formatBody(List rentals) {
17 | return rentals.stream().map(this::formatBodyLine).collect(joining());
18 | }
19 |
20 | private String formatFooter(List rentals) {
21 | return "Amount owed is " + calculateTotalPrice(rentals) + "\n" +
22 | "You earned " + calculateTotalRenterPoints(rentals) + " frequent renter points";
23 | }
24 |
25 | private double calculateTotalPrice(List rentals) {
26 | return rentals.stream().mapToDouble(Rental::calculatePrice).sum();
27 | }
28 |
29 | private int calculateTotalRenterPoints(List rentals) {
30 | return rentals.stream().mapToInt(Rental::calculateRenterPoints).sum();
31 | }
32 |
33 | private String formatBodyLine(Rental rental) {
34 | return "\t" + rental.getMovie().getTitle() + "\t" + rental.calculatePrice() + "\n";
35 | }
36 |
37 | private String formatHeader(String customerName) {
38 | return "Rental Record for " + customerName + "\n";
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/videostore/horror/StatementFormatterTest.java:
--------------------------------------------------------------------------------
1 | package videostore.horror;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import static java.util.Arrays.asList;
9 | import static org.junit.Assert.assertEquals;
10 |
11 |
12 | public class StatementFormatterTest {
13 |
14 | @Test
15 | public void characterizationTest() {
16 | List rentals = asList(
17 | new Rental(new Movie("Star Wars", Movie.Type.NEW_RELEASE), 6),
18 | new Rental(new Movie("Sofia", Movie.Type.CHILDRENS), 7),
19 | new Rental(new Movie("Inception", Movie.Type.REGULAR), 5));
20 |
21 | String expected = "Rental Record for John Doe\n"
22 | + " Star Wars 18.0\n"
23 | + " Sofia 7.5\n"
24 | + " Inception 6.5\n"
25 | + "Amount owed is 32.0\n"
26 | + "You earned 4 frequent renter points";
27 |
28 | assertEquals(expected, new StatementFormatter()
29 | .formatStatement("John Doe", rentals));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------