From 99e370ebc8717f751a54e37ee470c79ba0b009c7 Mon Sep 17 00:00:00 2001 From: haerong22 Date: Sat, 26 Feb 2022 15:55:30 +0900 Subject: [PATCH] refactoring : long function - split loop --- .../_12_split_loop/Participant.java | 23 ++++ .../_12_split_loop/StudyDashboard.java | 126 ++++++++++++++++++ .../_12_split_loop/StudyPrinter.java | 71 ++++++++++ 3 files changed, 220 insertions(+) create mode 100644 refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/Participant.java create mode 100644 refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyDashboard.java create mode 100644 refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyPrinter.java diff --git a/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/Participant.java b/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/Participant.java new file mode 100644 index 00000000..13df2c2c --- /dev/null +++ b/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/Participant.java @@ -0,0 +1,23 @@ +package com.example.refactoring._03_long_function._12_split_loop; + +import java.util.HashMap; +import java.util.Map; + +public record Participant(String username, Map homework) { + public Participant(String username) { + this(username, new HashMap<>()); + } + + public double getRate(double total) { + long count = this.homework.values().stream() + .filter(v -> v == true) + .count(); + return count * 100 / total; + } + + public void setHomeworkDone(int index) { + this.homework.put(index, true); + } + +} + diff --git a/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyDashboard.java b/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyDashboard.java new file mode 100644 index 00000000..70be5252 --- /dev/null +++ b/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyDashboard.java @@ -0,0 +1,126 @@ +package com.example.refactoring._03_long_function._12_split_loop; + +import org.kohsuke.github.GHIssue; +import org.kohsuke.github.GHIssueComment; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GitHub; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class StudyDashboard { + + private final int totalNumberOfEvents; + private final List participants; + private final Participant[] firstParticipantsForEachEvent; + + public StudyDashboard(int totalNumberOfEvents) { + this.totalNumberOfEvents = totalNumberOfEvents; + participants = new CopyOnWriteArrayList<>(); + firstParticipantsForEachEvent = new Participant[this.totalNumberOfEvents]; + } + + public static void main(String[] args) throws IOException, InterruptedException { + StudyDashboard studyDashboard = new StudyDashboard(15); + studyDashboard.print(); + } + + private void print() throws IOException, InterruptedException { + checkGithubIssues(getGhRepository()); + new StudyPrinter(this.totalNumberOfEvents, this.participants).execute(); + printFirstParticipants(); + } + + /** + * 하나의 반복문 안에 여러 작업을 수행하는 경우 각 작업을 따로 분리할 수 있다. + */ + private void checkGithubIssues(GHRepository ghRepository) throws InterruptedException { + ExecutorService service = Executors.newFixedThreadPool(8); + CountDownLatch latch = new CountDownLatch(totalNumberOfEvents); + + for (int index = 1 ; index <= totalNumberOfEvents ; index++) { + int eventId = index; + service.execute(new Runnable() { + @Override + public void run() { + try { + GHIssue issue = ghRepository.getIssue(eventId); + List comments = issue.getComments(); + + checkHomeWork(comments, eventId); + + Participant first = findFirst(comments); + firstParticipantsForEachEvent[eventId - 1] = first; + + latch.countDown(); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + }); + } + + latch.await(); + service.shutdown(); + } + + private Participant findFirst(List comments) throws IOException { + Date firstCreatedAt = null; + Participant first = null; + for (GHIssueComment comment : comments) { + Participant participant = findParticipant(comment.getUserName(), this.participants); + if (firstCreatedAt == null || comment.getCreatedAt().before(firstCreatedAt)) { + firstCreatedAt = comment.getCreatedAt(); + first = participant; + } + } + return first; + } + + private void checkHomeWork(List comments, int eventId) { + for (GHIssueComment comment : comments) { + Participant participant = findParticipant(comment.getUserName(), this.participants); + participant.setHomeworkDone(eventId); + } + } + + private void printFirstParticipants() { + Arrays.stream(this.firstParticipantsForEachEvent).forEach(p -> System.out.println(p.username())); + } + + private GHRepository getGhRepository() throws IOException { + GitHub gitHub = GitHub.connect(); + GHRepository repository = gitHub.getRepository("whiteship/live-study"); + return repository; + } + + private Participant findParticipant(String username, List participants) { + return isNewParticipant(username, participants) ? + createNewParticipant(username, participants) : + findExistingParticipant(username, participants); + } + + private Participant findExistingParticipant(String username, List participants) { + Participant participant; + participant = participants.stream().filter(p -> p.username().equals(username)).findFirst().orElseThrow(); + return participant; + } + + private Participant createNewParticipant(String username, List participants) { + Participant participant; + participant = new Participant(username); + participants.add(participant); + return participant; + } + + private boolean isNewParticipant(String username, List participants) { + return participants.stream().noneMatch(p -> p.username().equals(username)); + } + +} diff --git a/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyPrinter.java b/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyPrinter.java new file mode 100644 index 00000000..3a3a0d0a --- /dev/null +++ b/refactoring/src/main/java/com/example/refactoring/_03_long_function/_12_split_loop/StudyPrinter.java @@ -0,0 +1,71 @@ +package com.example.refactoring._03_long_function._12_split_loop; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Comparator; +import java.util.List; + +public class StudyPrinter { + + private int totalNumberOfEvents; + + private List participants; + + public StudyPrinter(int totalNumberOfEvents, List participants) { + this.totalNumberOfEvents = totalNumberOfEvents; + this.participants = participants; + } + + public void execute() throws IOException { + try (FileWriter fileWriter = new FileWriter("participants.md"); + PrintWriter writer = new PrintWriter(fileWriter)) { + this.participants.sort(Comparator.comparing(Participant::username)); + + writer.print(header(this.participants.size())); + + this.participants.forEach(p -> { + String markdownForHomework = getMarkdownForParticipant(p); + writer.print(markdownForHomework); + }); + } + } + + private String getMarkdownForParticipant(Participant p) { + return String.format("| %s %s | %.2f%% |\n", p.username(), checkMark(p, this.totalNumberOfEvents), + p.getRate(this.totalNumberOfEvents)); + } + + /** + * | 참여자 (420) | 1주차 | 2주차 | 3주차 | 참석율 | + * | --- | --- | --- | --- | --- | + */ + private String header(int totalNumberOfParticipants) { + StringBuilder header = new StringBuilder(String.format("| 참여자 (%d) |", totalNumberOfParticipants)); + + for (int index = 1; index <= this.totalNumberOfEvents; index++) { + header.append(String.format(" %d주차 |", index)); + } + header.append(" 참석율 |\n"); + + header.append("| --- ".repeat(Math.max(0, this.totalNumberOfEvents + 2))); + header.append("|\n"); + + return header.toString(); + } + + /** + * |:white_check_mark:|:white_check_mark:|:white_check_mark:|:x:| + */ + private String checkMark(Participant p, int totalEvents) { + StringBuilder line = new StringBuilder(); + for (int i = 1 ; i <= totalEvents ; i++) { + if(p.homework().containsKey(i) && p.homework().get(i)) { + line.append("|:white_check_mark:"); + } else { + line.append("|:x:"); + } + } + return line.toString(); + } +}