├── .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 | --------------------------------------------------------------------------------