diff --git a/README.md b/README.md index 5ef72c62..9ac34405 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Here are the different challenges : - [Day 13: Find a way to eliminate the irrelevant, and amplify the essentials of those tests.](exercise/day13/docs/challenge.md) - [Day 14: Do not use exceptions anymore.](exercise/day14/docs/challenge.md) - [Day 15: Put a code under tests.](exercise/day15/docs/challenge.md) +- [Day 16: Make this code immutable.](exercise/day16/docs/challenge.md) ### Solutions A solution proposal will be published here every day during the `Advent Of Craft` containing `the code` and a `step by step` guide. diff --git a/exercise/day16/docs/challenge.md b/exercise/day16/docs/challenge.md new file mode 100644 index 00000000..118ce79f --- /dev/null +++ b/exercise/day16/docs/challenge.md @@ -0,0 +1,25 @@ +## Day 16: Withstanding the change of time. + +Today, you are passing through a agitated sea. +A tornado is raging not far from your area, and you have to change course. + +Today's exercise is about sustaining code. +A code that is robust and that will weather changes. + +There are different avenues for code improvement: + +- We looked at breaking conditions, better naming, avoiding complexity + and even dependency check. + +But we are not talking about these refactoring steps. +No, today we focus our effort into a simple question: + +_Is my code better mutable or immutable?_ + +> **Day 16: Make this code immutable.** + +May you be protected on your crafting journey! + +- 💡HINT: Code from usage for this one. + +![snippet of the day](snippet.png) \ No newline at end of file diff --git a/exercise/day16/docs/snippet.png b/exercise/day16/docs/snippet.png new file mode 100644 index 00000000..f49679ba Binary files /dev/null and b/exercise/day16/docs/snippet.png differ diff --git a/exercise/day16/pom.xml b/exercise/day16/pom.xml new file mode 100644 index 00000000..7ab0f9cb --- /dev/null +++ b/exercise/day16/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + + com.advent-of-craft + advent-of-craft2023 + 1.0-SNAPSHOT + + + blog-part3 + 1.0-SNAPSHOT + + 3.0.0 + + + + + org.instancio + instancio-junit + ${instancio-junit.version} + test + + + + \ No newline at end of file diff --git a/exercise/day16/src/main/java/blog/Article.java b/exercise/day16/src/main/java/blog/Article.java new file mode 100644 index 00000000..c96b8772 --- /dev/null +++ b/exercise/day16/src/main/java/blog/Article.java @@ -0,0 +1,37 @@ +package blog; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +public class Article { + private final String name; + private final String content; + private final ArrayList comments; + + public Article(String name, String content) { + this.name = name; + this.content = content; + this.comments = new ArrayList<>(); + } + + private void addComment( + String text, + String author, + LocalDate creationDate) throws CommentAlreadyExistException { + var comment = new Comment(text, author, creationDate); + + if (comments.contains(comment)) { + throw new CommentAlreadyExistException(); + } else comments.add(comment); + } + + public void addComment(String text, String author) throws CommentAlreadyExistException { + addComment(text, author, LocalDate.now()); + } + + public List getComments() { + return comments; + } +} + diff --git a/exercise/day16/src/main/java/blog/Comment.java b/exercise/day16/src/main/java/blog/Comment.java new file mode 100644 index 00000000..c6b25bfc --- /dev/null +++ b/exercise/day16/src/main/java/blog/Comment.java @@ -0,0 +1,6 @@ +package blog; + +import java.time.LocalDate; + +public record Comment(String text, String author, LocalDate creationDate) { +} diff --git a/exercise/day16/src/main/java/blog/CommentAlreadyExistException.java b/exercise/day16/src/main/java/blog/CommentAlreadyExistException.java new file mode 100644 index 00000000..1eaca1d1 --- /dev/null +++ b/exercise/day16/src/main/java/blog/CommentAlreadyExistException.java @@ -0,0 +1,4 @@ +package blog; + +public class CommentAlreadyExistException extends Exception { +} \ No newline at end of file diff --git a/exercise/day16/src/test/java/blog/ArticleBuilder.java b/exercise/day16/src/test/java/blog/ArticleBuilder.java new file mode 100644 index 00000000..f1d49c83 --- /dev/null +++ b/exercise/day16/src/test/java/blog/ArticleBuilder.java @@ -0,0 +1,34 @@ +package blog; + +import java.util.HashMap; +import java.util.Map; + +public class ArticleBuilder { + public static final String AUTHOR = "Pablo Escobar"; + public static final String COMMENT_TEXT = "Amazing article !!!"; + private final HashMap comments; + + public ArticleBuilder() { + comments = new HashMap<>(); + } + + public static ArticleBuilder anArticle() { + return new ArticleBuilder(); + } + + public ArticleBuilder commented() { + this.comments.put(COMMENT_TEXT, AUTHOR); + return this; + } + + public Article build() throws CommentAlreadyExistException { + var article = new Article( + "Lorem Ipsum", + "consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore" + ); + for (Map.Entry comment : comments.entrySet()) { + article.addComment(comment.getKey(), comment.getValue()); + } + return article; + } +} diff --git a/exercise/day16/src/test/java/blog/ArticleTests.java b/exercise/day16/src/test/java/blog/ArticleTests.java new file mode 100644 index 00000000..68f05fdf --- /dev/null +++ b/exercise/day16/src/test/java/blog/ArticleTests.java @@ -0,0 +1,73 @@ +package blog; + +import org.assertj.core.api.ThrowingConsumer; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.function.Function; + +import static blog.ArticleBuilder.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.instancio.Instancio.create; + +class ArticleTests { + private Article article; + + @Test + void should_add_comment_in_an_article() throws CommentAlreadyExistException { + when(article -> article.addComment(COMMENT_TEXT, AUTHOR)); + then(article -> { + assertThat(article.getComments()).hasSize(1); + assertComment(article.getComments().get(0), COMMENT_TEXT, AUTHOR); + }); + } + + @Test + void should_add_comment_in_an_article_containing_already_a_comment() throws Throwable { + final var newComment = create(String.class); + final var newAuthor = create(String.class); + + when(ArticleBuilder::commented, article -> article.addComment(newComment, newAuthor)); + then(article -> { + assertThat(article.getComments()).hasSize(2); + assertComment(article.getComments().getLast(), newComment, newAuthor); + }); + } + + @Nested + class Fail { + @Test + void when__adding_an_existing_comment() throws CommentAlreadyExistException { + var article = anArticle() + .commented() + .build(); + + assertThatThrownBy(() -> { + article.addComment(article.getComments().get(0).text(), article.getComments().get(0).author()); + }).isInstanceOf(CommentAlreadyExistException.class); + } + } + + private static void assertComment(Comment comment, String commentText, String author) { + assertThat(comment.text()).isEqualTo(commentText); + assertThat(comment.author()).isEqualTo(author); + } + + private void when(ArticleBuilder articleBuilder, ThrowingConsumer
act) throws CommentAlreadyExistException { + article = articleBuilder.build(); + act.accept(article); + } + + private void when(ThrowingConsumer
act) throws CommentAlreadyExistException { + when(anArticle(), act); + } + + private void when(Function options, ThrowingConsumer
act) throws Throwable { + when(options.apply(anArticle()), act); + } + + private void then(ThrowingConsumer
act) { + act.accept(article); + } +} \ No newline at end of file diff --git a/exercise/pom.xml b/exercise/pom.xml index e99d88a5..617e134c 100644 --- a/exercise/pom.xml +++ b/exercise/pom.xml @@ -24,6 +24,7 @@ day13 day14 day15 + day16