목표
  • 자바가 제공하는 제어문을 학습하세요.
학습할 것
  • 선택문
  • 반복문
과제
  • 과제 0. JUnit 5 학습하세요.
  • 과제 1. live-study 대시 보드를 만드는 코드를 작성하세요.
  • 과제 2. LinkedList를 구현하세요.
  • 과제 3. Stack을 구현하세요.
  • 과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.
  • 과제 5. Queue를 구현하세요.

선택문

자바는 순차적으로 위에서 아래로 차례대로 실행합니다. 실행 순서는 조건에 의해 변경할 수 있습니다. 실행 순서를 변경하기 위해 제어문(Control Statement)을 사용합니다.

제어문은 다음과 같이 구분합니다.

1) 선택문: if 문, if ~ else 문, switch 문
2) 반복문: while 문, do ~ while 문, for 문
3) 점프문: goto 문, continue 문, break 문

특정한 기준(조건)에 해당할 때 지정한 문장을 실행할 것인지 안할 것인지 결정할 때 선택문(조건문)을 사용합니다.

if 문

if 문은 "만약 ~ 이라면(if)을 하고 그렇지 않으면(else) ~를 한다" 라는 논리 구조로 만약 ~ 부분의 조건을 조건식으로 표기합니다.

if(조건식) {
    // 조건식이 맞으면 수행할 문장
}

if ~ else 문

if 문은 else와 함께 사용하여 하나의 세트 문장으로 인식합니다. else는 false 선택일 때 처리하기 위해서 사용합니다.

if (조건식) {
    // 조건식이 맞으면 수행할 문장
} else {
    // 조건식이 안 맞으면 수행할 문장
}

중첩 if 문

else 문 안에 또 다른 if 문을 포함하는 것을 중첩 if 문이라고 합니다. 여러 조건에 의해 선택하는 경우에 중첩합니다.

if 문은 또 다른 if 문이나 if ~ else 문을 포함한 어떤 문장도 얼마든지 중첩할 수 있습니다.

if (조건식1) {
    // 위의 조건식과 맞으면 수행할 문장
} else if (조건식2) {
    // 위의 조건식과 맞으면 수행할 문장
} else {
    // 나머지의 경우 수행할 문장
}

switch 문

조건문의 경우의 수가 많은 경우에는 if 문 대신 switch 문을 사용하면 간결하고 가독성이 높습니다. switch 문은 다중 선택문 입니다.

switch (식) {
    case 정수1:  
        // 식의 결과 값이 정수1이면 실행할 문장1;
        break;

    case 정수2:  
        // 식의 결과 값이 정수2이면 실행할 문장2;
        break;

    case 정수n:  
        // 식의 결과 값이 정수n이면 실행할 문장n;
        break;

    default:       
        // 옵션, 식의 결과 값과 일치하는 case 문이 없을 때 실행할 문장

}

반복문

사람이 하기 힘든, 귀찮은 반복적인 일을 반복문을 통해 기계가 대신 수행할 수 있습니다.

while

while 문은 반복 조건이 참(true)이면 중괄호 구간을 반복적으로 실행하고 조건이 거짓(false)이면 반복문을 실행하지 않고 빠져나옵니다.

while(조건){
    // 반복 실행 영역
}
int i = 0;
while(i < 5){         
    System.out.println(i);
    i++;
}
// 0
// 1
// 2
// 3
// 4
  • i의 값이 10보다 작다면 true, 크다면 false가 됩니다. 구간 내 작업을 수행하고 크기가 증가하는 i의 값을 while문의 조건절에서 매번 확인하고 계속 반복을 해야하는지를 판단합니다.

for

for문은 특정한 횟수만큼 반복 실행을 하는 경우에 자주 사용됩니다.

for(초기화; 종료조건; 반복실행){
    반복적으로 실행될 구문
}
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}
// 0
// 1
// 2
// 3
// 4
  • 초기화 : 반복문이 실행될 때 1회 실행됩니다.
  • 종료조건 : 초기화가 실행된 후에 종료조건이 실행됩니다. 종료조건의 값이 false일 때까지 반복문의 중괄호 구간의 코드가 반복 실행됩니다.
  • 중괄호 구간의 실행이 끝나면 반복 실행이 실행됩니다. 일반적으로 이 곳에 i++와 같이 변수를 증가시키는 로직이 위치하고, 이것이 실행된 후에 종료조건이 실행됩니다. 종료조건이 false가 될 때까지 이 과정이 반복됩니다.

break

반복 작업을 중간에 중단시키고 싶다면 break를 사용하면 됩니다.

for (int i = 0; i < 5; i++) {
    if (i == 3)
        break;
    System.out.println("i");
}
// 0
// 1
// 2

continue

현재 반복 작업을 건너뛰고 다음 차례 반복 작업을 하고 싶다면 continue를 사용하면 됩니다.

for (int i = 0; i < 5; i++) {
    if (i == 2)
        continue;
    System.out.println("Coding Everybody " + i);
}

// 0
// 1
// 3
// 4

과제 0. JUnit 5 학습하세요.

목표
  • 인텔리J, 이클립스, VS Code에서 JUnit 5로 테스트 코드 작성하는 방법에 익숙해 질 것.
  • 이미 JUnit 알고 계신분들은 다른 것 아무거나!
  • 더 자바, 테스트 강의도 있으니 참고하세요~

기본 Annotation

@DisplayName

  • 테스트 클래스 또는 테스트 메서드의 이름을 정의할 수 있습니다.

@Disable

  • 테스트 클래스 또는 메서드를 비활성화할 수 있습니다.

@BeforeAll

  • 해당 annotation 이 달린 메서드가 현재 클래스의 모든 테스트 메서드보다 먼저 실행됩니다.
  • 해당 메서드는 static 이어야 합니다.

@BeforeEach

  • 해당 annotation 이 달린 메서드가 각 테스트 메서드 전에 실행됩니다.

@AfterAll

  • 해당 annotation 이 달린 메서드가 현재 클래스의 모든 테스트 메소드보다 이후에 실행됩니다.
  • 해당 메서드는 static 이어야 합니다.

@AfterEach

  • 해당 annotation 이 달린 메서드가 각 테스트 메서드 이후에 실행됩니다.

Assertions and Assumptions

Assertions

  • Assert 구문은 어떤 조건이 참인지 검증하는 방법이다. 단언한 조건이 참이 아니면 테스트는 그 자리에서 멈추고 실패합니다.
  • assertTrue, assertThat 등이 있습니다.
  • assertAll()을 사용하여 assertions 을 그룹화하여 그룹 내에서 실패한 assertions 을 MultipleFailuresError 와 함께 기록할 수 있습니다.

Assumptions

  • 특정 조건이 충족되는 경우에만 테스트를 실행하는 데 사용됩니다.
  • 일반적으로 테스트가 제대로 실행되기 위해 필요한 외부 조건에 사용됩니다.
  • 테스트와 직접적인 관련은 없습니다.
  • assumptions 이 실패하면 TestAbortedException이 발생하고 테스트는 수행되지 않습니다.
  • assumeTrue(), assumeFalse(), assumingThat() 등이 있습니다.

Exception Testing

assertThrows

  • 발생한 예외의 세부 사항을 확인
    @Test
    void shouldThrowException() {
        Throwable exception = assertThrows(UnsupportedOperationException.class, () -> {
            throw new UnsupportedOperationException("Not supported");
        });
        assertEquals(exception.getMessage(), "Not supported");
    }
  • 예외 유형의 유효성을 검사
    @Test
    void assertThrowsException() {
        String str = null;
        assertThrows(IllegalArgumentException.class, () -> {
            Integer.valueOf(str);
        });
    }

Test Suites

@SelectPackages

  • Test Suites 를 실행할 때 선택할 패키지 이름 지정합니다.

SelectClasses

  • Test Suites 를 실행할 때 선택할 클래스 지정합니다.
  • 각 테스트는 하나의 패키지에 있지 않아도 됩니다.

Dynamic Tests

@TestFactory

  • 해당 annotation 이 달린 메서드는 동적 테스트를 위한 test factory 메서드입니다.

  • 런타임에 생성된 테스트 케이스를 선언하고 실행할 수 있습니다.

  • 각각 in, out 이라는 두 개의 ArrayList 를 사용하여 단어를 번역합니다.

  • @TestFactory
    public Stream<DynamicTest> translateDynamicTestsFromStream() {
        return in.stream()
          .map(word ->
              DynamicTest.dynamicTest("Test translate " + word, () -> {
                int id = in.indexOf(word);
                assertEquals(out.get(id), translate(word));
              })
        );
    }
    • @TestFactory 메서드는 private 또는 static 이면 안됩니다.
    • 테스트 수는 동적이며, ArrayList 크기에 따라 달라집니다.
    • @TestFactory 메서드는 Stream, Collection, Iterable 또는 Iterator 를 return 해야 합니다.

과제 1. live-study 대시 보드를 만드는 코드를 작성하세요.

목표
  • 깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.
  • 참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.
  • Github 자바 라이브러리를 사용하면 편리합니다.
  • 깃헙 API를 익명으로 호출하는데 제한이 있기 때문에 본인의 깃헙 프로젝트에 이슈를 만들고 테스트를 하시면 더 자주 테스트할 수 있습니다.
Github.java
public class Github {

    private static final String LIVE_STUDY_PATH = "whiteship/live-study";
    private static final int TOTAL_NUMBER_OF_ISSUE = 18;

    private GitHub github;

    public Github(String token) throws IOException {
        github = new GitHubBuilder().withOAuthToken(token).build();
    }

    public List<GithubUserReport> getGithubUserReportOfParticipation() throws IOException {
        Map<String, Integer> numberOfParticipationCounter = new HashMap<>();

        List<GHIssue> issues = github.getRepository(LIVE_STUDY_PATH).getIssues(GHIssueState.ALL);
        for (GHIssue issue : issues) {
            Set<String> notDuplicatedUser = getNotDuplicatedUserCommentedInIssue(issue);
            updateNumberOfParticipationCounter(numberOfParticipationCounter, notDuplicatedUser);
        }

        return getGithubUserReportOfParticipation(numberOfParticipationCounter);
    }

    private Set<String> getNotDuplicatedUserCommentedInIssue(GHIssue issue) throws IOException {
        Set<String> notDuplicatedUser = new HashSet<>();
        for (GHIssueComment ghIssueComment : issue.getComments()) {
            String user = ghIssueComment.getUser().getLogin();
            notDuplicatedUser.add(user);
        }

        return notDuplicatedUser;
    }

    private void updateNumberOfParticipationCounter(Map<String, Integer> counter, Set<String> notDuplicatedUser) {
        for (String user : notDuplicatedUser) {
            if (counter.containsKey(user)){
                counter.put(user, counter.get(user) + 1);
            } else {
                counter.put(user, 1);
            }
        }
    }

    private List<GithubUserReport> getGithubUserReportOfParticipation(Map<String, Integer> counter) {
        return counter.entrySet().stream()
            .map(e -> {
                String username = e.getKey();
                int numberOfParticipation = e.getValue();
                double rate = calculateRateOfParticipant(e.getValue());

                return new GithubUserReport(username, numberOfParticipation, rate);
            })
            .sorted(Comparator.comparing(GithubUserReport::getUsername))
            .collect(Collectors.toList());
    }

    private double calculateRateOfParticipant(int value) {
        return (double) (value * 100) / TOTAL_NUMBER_OF_ISSUE;
    }
}
GithubUserReport.java
public class GithubUserReport {

    private final String username;
    private final int numberOfParticipation;
    private final double rateOfParticipation;

    public GithubUserReport(String username, int numberOfParticipation, double rateOfParticipation) {
        this.username = username;
        this.numberOfParticipation = numberOfParticipation;
        this.rateOfParticipation = rateOfParticipation;
    }

    public String getUsername() {
        return username;
    }

    public int getNumberOfParticipation() {
        return numberOfParticipation;
    }

    public double getRateOfParticipation() {
        return rateOfParticipation;
    }
}
GithubTest.java
class GithubTest {

    @Test
    void printGithubUserReportOfParticipation() throws IOException {
        final String TITLE_ALIGNMENT = "| %20s | %5s | %5s |";
        final int WIDTH = 45;
        final String ROW_ALIGNMENT = "| %20s | %8d | %8.2f |";
        final String HYPHEN = "-";
        final String TOKEN = "github 토큰을 넣으면 됩니당";

        Github github = new Github(TOKEN);

        System.out.println(HYPHEN.repeat(WIDTH));
        System.out.println(String.format(TITLE_ALIGNMENT, "Github ID", "참여 횟수", "참여율(%)"));
        System.out.println(HYPHEN.repeat(WIDTH));

        List<GithubUserReport> userReports = github.getGithubUserReportOfParticipation();
        userReports.forEach(userReport -> {
            System.out.println(
                String.format(ROW_ALIGNMENT,
                    userReport.getUsername(),
                    userReport.getNumberOfParticipation(),
                    userReport.getRateOfParticipation()));
        });

        System.out.println(HYPHEN.repeat(WIDTH));
    }
}

---------------------------------------------
|            Github ID | 참여 횟수 | 참여율(%) |
---------------------------------------------
|          0417taehyun |        3 |    16.67 |
|             1031nice |        3 |    16.67 |
|              2yeseul |        2 |    11.11 |
|               372dev |        3 |    16.67 |
|              9m1i9n1 |        3 |    16.67 |
|              Ahnyezi |        1 |     5.56 |
|              BaeJi77 |        2 |    11.11 |
|           ByungJun25 |        3 |    16.67 |
|              CODEMCD |        3 |    16.67 |
...

과제 2. LinkedList를 구현하세요.

목표
  • LinkedList에 대해 공부하세요.
  • 정수를 저장하는 ListNode 클래스를 구현하세요.
  • ListNode add(ListNode head, ListNode nodeToAdd, int position)를 구현하세요.
  • ListNode remove(ListNode head, int positionToRemove)를 구현하세요.
  • boolean contains(ListNode head, ListNode nodeTocheck)를 구현하세요.

과제 3. Stack을 구현하세요.

목표
  • int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.

목표
  • ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

과제 5. Queue를 구현하세요.

목표
  • 배열을 사용해서 한번
  • ListNode를 사용해서 한번.
출처

+ 따끈한 최근 게시물