27866 문자와 문자열
[ charAt vs substring ]
- charAt(index)는 특정 위치의 문자를 바로 가져옵니다
	ex. S.charAt(i - 1)
- substring(start, end)는 문자열의 특정 구간을 잘라냅니다
	ex. S.substring(i-1,i)
9086 문자열
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        sc.nextLine(); // 개행문자제거

        StringBuilder result = new StringBuilder(); // 결과 저장

        for (int i = 0; i < N; i++) {
            String S = sc.nextLine();
            int length = S.length();
            result.append(S.charAt(0)).append(S.charAt(length - 1)).append("\n");
        }
        System.out.println(result);     // 한 번에 출력
    }
}
11720 숫자의 합
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt(); // 숫자의 개수
        String numbers = sc.next(); // 공백 없이 쓰인 숫자들

        int sum = 0;

        // 숫자 문자열의 각 문자를 정수로 변환하여 합산
        for (int i = 0; i < N; i++) {
            System.out.println("numbers.charAt(i) = " + numbers.charAt(i));
            sum += numbers.charAt(i) - '0'; // char을 int로 변환
        }

        System.out.println(sum); // 합 출력
    }
}
10809 알파벳 찾기
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String S = sc.nextLine();

        StringBuilder result = new StringBuilder();

       /* 'a' 부터 'z' 까지 반복
          S.indexOf(c):
		  c가 S에 포함되어 있으면 첫 등장 위치를 반환
		  포함되어 있지 않으면 -1을 반환*/
        for (char c = 'a'; c <= 'z'; c++) {
            result.append(S.indexOf(c)).append(" ");
        }

//        마지막 공백을 제거한 뒤 출력
        System.out.println(result.toString().trim());

    }
}

2908
상수
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 입력받기
        int A = sc.nextInt();
        int B = sc.nextInt();

        // 숫자 뒤집기
        int reversedA = reverseNumber(A);
        int reversedB = reverseNumber(B);

        // 결과 출력
        System.out.println(Math.max(reversedA, reversedB));
    }

    // 숫자를 거꾸로 만드는 메서드
    private static int reverseNumber(int num) {
        int reversed = 0;
        while (num > 0) {
            reversed = reversed * 10 + (num % 10); // 가장 오른쪽 자리를 추가
            num /= 10; // 오른쪽 자리 제거
        }
        return reversed;
    }
}
11718 그대로 출력하기
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args)  throws IOException {
        Scanner sc = new Scanner(System.in);

//        1. Scanner를 사용하는 경우
        while (sc.hasNextLine()) { // 입력이 더 있는 동안 반복
            String line = sc.nextLine(); // 한 줄 읽기
            System.out.println(line); // 그대로 출력
        }

//        2. BufferedReader를 사용하는 경우
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;

        while ((line = br.readLine()) != null) { // 입력이 null(EOF)일 때 종료
            System.out.println(line); // 그대로 출력
        }
    }
}

 

'Algorithm' 카테고리의 다른 글

[백준] 1차원 배열  (0) 2024.11.25
[백준] 반복문  (0) 2024.11.24
[백준] 조건문  (0) 2024.11.23
[백준] 입출력과 사칙연산  (0) 2024.11.23
프로그래머스 Lv.0  (0) 2023.06.18
10810 공 넣기
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();

        int[] baskets = new int[N];

        for (int m = 0; m < M; m++) {
            int i = sc.nextInt();
            int j = sc.nextInt();
            int k = sc.nextInt();

            for (int b = i - 1; b < j; b++) {
                baskets[b] = k;
            }
        }
        for (int b = 0; b < N; b++) {
            System.out.print(baskets[b] + " ");
        }

    }
}
10813 공 바꾸기
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();


        // 바구니 초기화
        int[] baskets = new int[N];
        for (int i = 0; i < N; i++) {
            baskets[i] = i + 1;
        }

        for (int a = 0; a < M; a++) {
            int i = sc.nextInt();
            int j = sc.nextInt();
            // i번 바구니와 j번 바구니의 공을 교환 (인덱스는 0부터 시작하므로 i-1, j-1)
            int temp = baskets[i - 1];
            baskets[i - 1] = baskets[j - 1];
            baskets[j - 1] = temp;
        }

        for (int b = 0; b < N  ; b++) {
            System.out.print(baskets[b] + " ");
        }

    }
}
5597 과제 안 내신 분..?
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        boolean[] submitted = new boolean[31];

        for (int i = 0; i < 28; i++) {
            int N = sc.nextInt();
            submitted[N] = true;
        }

        for (int i = 1; i <= 30; i++) {
            if (!submitted[i]) {
                System.out.println(i);
            }
        }
    }
}

3052
나머지

 

* HashSet은 중복 값을 허용하지 않는 자료구조
* 시간 복잡도는 O(1) 삽입 연산

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        HashSet<Integer> remainders = new HashSet<>(); // 나머지를 저장할 HashSet

        for (int i = 0; i < 10; i++) {
            int num = sc.nextInt();
            remainders.add(num % 42); // 42로 나눈 나머지를 HashSet에 추가
        }

        // HashSet의 크기가 서로 다른 나머지 값의 개수
        System.out.println(remainders.size());
    }
}
10811 바구니 뒤집기
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt(); // 바구니 개수
        int M = sc.nextInt(); // 뒤집기 명령 수

        // 바구니 초기화
        int[] baskets = new int[N];
        for (int i = 0; i < N; i++) {
            baskets[i] = i + 1;
        }

        for (int a = 0; a < M; a++) {
            int i = sc.nextInt(); // 시작 바구니
            int j = sc.nextInt(); // 끝 바구니

            reverse(baskets, i - 1, j - 1);
        }

        // 결과 출력
        for (int num : baskets) {
            System.out.print(num + " ");
        }
    }

    // 배열의 부분 구간 [start, end]를 역순으로 뒤집는 함수
    private static void reverse(int[] arr, int start, int end) {
        while (start < end) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            start++;
            end--;
        }
    }
}

1546
평균
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt(); // 시험 본 과목의 갯수
        double[] scores = new double[N];

        double maxScore = 0;
        for (int i = 0; i < N; i++) {
            scores[i] = sc.nextDouble();
            if (scores[i] > maxScore) {
                maxScore = scores[i];  // 최댓값 갱신
            }
        }
        // 새로운 점수 계산
        double sum = 0; // 변환 점수의 합
        for (int i = 0; i < N; i++) {
            sum += (scores[i] / maxScore) * 100;
        }
        // 새로운 평균 계산
        double newAverage = sum / N;
        System.out.println(newAverage);

    }
}

'Algorithm' 카테고리의 다른 글

[백준] 문자열  (0) 2024.11.26
[백준] 반복문  (0) 2024.11.24
[백준] 조건문  (0) 2024.11.23
[백준] 입출력과 사칙연산  (0) 2024.11.23
프로그래머스 Lv.0  (0) 2023.06.18

 

8393
1부터 n까지의 합
n * (n + 1) / 2
25314 코딩은 체육과목 입니다

 

 [ StringBuilder ]
 - 가변 객체(Mutable Object)
 - 문자열이 변경될 때 새로운 객체를 생성하지 않고, 기존 객체 내부에서 내용을 변경
 - 성능과 메모리 측면에서 매우 효율적
 
1.	가변성:
•	문자열의 추가, 삭제, 변경 작업을 기존 객체에서 수행
2.	효율성:
•	반복적인 문자열 수정 작업에서 메모리를 덜 사용하고, 속도가 빠름
3.	메서드 제공:
•	문자열 추가 (append), 삭제 (delete), 삽입 (insert), 반전 (reverse) 등의 다양한 메서드를 제공
15552 빠른 A+B
* Scanner와 System.out.println 대신 BufferedReader와 BufferedWriter를 사용
[ BufferedReader와 BufferedWriter ]

1. BufferedReader 사용:
• BufferedReader는 입력을 버퍼링하여 대량의 입력을 효율적으로 처리
• readLine()을 사용하여 한 줄씩 읽음

2. BufferedWriter 사용:
• BufferedWriter는 출력 스트림을 버퍼링하여 출력 작업을 최적화
• write()를 사용하여 출력 내용을 기록하고, \n으로 줄바꿈을 추가

4. 출력 최적화:
• 반복문 안에서 출력할 내용을 BufferedWriter에 기록
• 출력 스트림을 비우기 위해 마지막에 flush()를 호출
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int T = Integer.parseInt(br.readLine());

        for (int i = 0; i < T; i++) {
            String[] input = br.readLine().split(" ");
            int A = Integer.parseInt(input[0]);
            int B = Integer.parseInt(input[1]);
            bw.write((A + B) + "\n");
        }
        bw.flush();
        bw.close();
        br.close();

    }
}
2439 별 찍기 - 2
        for (int i = 1; i <= N; i++) {
            // 공백 출력 (N - i 개)
            for (int j = 1; j <= N - i; j++) {
                System.out.print(" ");
            }
            // 별 출력 (i 개)
            for (int j = 1; j <= i; j++) {
                System.out.print("*");
            }
            // 줄바꿈
            System.out.println();
        }

'Algorithm' 카테고리의 다른 글

[백준] 문자열  (0) 2024.11.26
[백준] 1차원 배열  (0) 2024.11.25
[백준] 조건문  (0) 2024.11.23
[백준] 입출력과 사칙연산  (0) 2024.11.23
프로그래머스 Lv.0  (0) 2023.06.18
2884 알람 시계
(0 ≤ H ≤ 23, 0 ≤ M ≤ 59) 

    	// 45분 앞당기기
        M -= 45;

        // 만약 분(M)이 음수라면
        if (M < 0) {
            M += 60; // 60분을 더해서 양수로 만듦
            H -= 1; // 한 시간을 빼줌
        }

        // 만약 시간이 음수라면
        if (H < 0) {
            H += 24; // 24를 더해서 하루의 끝으로 되돌림
        }

 

2525 오븐 시계

 

        // 총 분 계산
        B += C; // 현재 분에 요리 시간을 더함
        A += B / 60; // 총 분에서 시간을 추가
        B %= 60; // 남은 분 계산
        A %= 24; // 24시간 기준으로 시간 계산


	1.	현재 분과 요리 시간 더하기:
			B += C: 현재 분(B)에 요리 시간(C)을 더합니다.
	2.	시간 계산:
			A += B / 60: 분(B)을 60으로 나눈 몫을 시간(A)에 추가합니다.
			B %= 60: 분(B)에서 60으로 나눈 나머지를 계산하여 남은 분을 구합니다.
	3.	24시간 기준으로 시간 계산:
			A %= 24: 24시간을 초과하면 시간을 다시 0시부터 계산합니다.

 

'Algorithm' 카테고리의 다른 글

[백준] 1차원 배열  (0) 2024.11.25
[백준] 반복문  (0) 2024.11.24
[백준] 입출력과 사칙연산  (0) 2024.11.23
프로그래머스 Lv.0  (0) 2023.06.18
프로그래머스 Lv.0  (0) 2023.06.10
2588 곱셈

 

	// 첫 번째 세 자리 자연수 입력받기
        int num1 = sc.nextInt();
        // 두 번째 세 자리 자연수 입력받기
        int num2 = sc.nextInt();

        // (3) 두 번째 숫자의 1의 자리와 첫 번째 숫자를 곱한 값
        System.out.println(num1 * (num2 % 10));
        // (4) 두 번째 숫자의 10의 자리와 첫 번째 숫자를 곱한 값
        System.out.println(num1 * ((num2 / 10) % 10));
        // (5) 두 번째 숫자의 100의 자리와 첫 번째 숫자를 곱한 값
        System.out.println(num1 * (num2 / 100));
        // (6) 두 숫자의 곱
        System.out.println(num1 * num2);

 

11382 꼬마 정민

 

[ 런타임 에러 ] 	

  입력값 A, B, C는 최대 10^{12}까지 
  int 타입은 32비트 정수로 범위가 -2^{31}에서 2^{31}-1까지
 
 int 대신 long을 사용

 

'Algorithm' 카테고리의 다른 글

[백준] 반복문  (0) 2024.11.24
[백준] 조건문  (0) 2024.11.23
프로그래머스 Lv.0  (0) 2023.06.18
프로그래머스 Lv.0  (0) 2023.06.10
프로그래머스 Lv.0  (0) 2023.06.09

"OS kernel"에서의 backlog는 주로 네트워크 프로그래밍의 컨텍스트에서 사용됩니다. 이는 소켓 프로그래밍에서 서버가 클라이언트로부터의 연결 요청을 얼마나 많이 대기열에 둘 수 있는지를 나타내는 값입니다.

먼저, 간단한 네트워크 연결 과정을 이해하면 좋습니다:

  • 서버는 특정 포트에서 클라이언트의 연결을 기다리기 위해 listen 상태가 됩니다.
  • 클라이언트는 해당 포트로 연결 요청을 보냅니다.
  • 서버는 연결 요청을 받아들이기 전에 이 요청을 일시적으로 대기열에 넣습니다. 이 대기열의 크기가 backlog입니다.

backlog의 값은 listen() 시스템 호출에서 지정됩니다. 예를 들어, C 언어의 소켓 프로그래밍에서 listen(sockfd, 5);라는 호출이 있다면, 서버는 동시에 최대 5개의 연결 요청을 대기열에 넣을 수 있습니다.

만약 backlog 값이 5이고, 동시에 6개의 연결 요청이 들어오면, 6번째 연결 요청은 거절될 수 있습니다.

따라서, backlog는 고성능 서버나 동시에 많은 연결 요청이 예상되는 서비스에서는 적절히 설정해주어야 합니다. 너무 낮게 설정하면 동시에 들어오는 많은 연결 요청 중 일부가 거절될 위험이 있습니다. 너무 높게 설정하면, 필요 이상의 리소스가 낭비될 수 있습니다.

이런 방식으로, OS kernel backlog 서버의 연결 수용 능력을 조절하는데 사용됩니다

 

 

backlog는 실질적으로 컴퓨터 네트워크에서 대기열(queue)을 의미합니다. 네트워킹 쪽에서 대기열이란, 데이터나 연결 요청을 일시적으로 저장하는 장소를 의미합니다. 이 대기열은 한정된 크기를 가지기 때문에, 이 크기를 초과하는 데이터나 연결 요청이 들어올 경우 추가적인 데이터나 요청은 버려지거나 거절될 수 있습니다.

그런데 여기서 backlog는 주로 두 가지 상황에서 사용됩니다:

  • 네트워크 장치의 패킷 대기열 (netdev_max_backlog):데이터가 네트워크를 통해 전송될 때, 각각의 데이터는 패킷(packet)이라는 작은 단위로 나뉩니다. 이러한 패킷들은 운영체제의 네트워크 스택에 도착하면 일시적으로 큐에 저장됩니다.
  • netdev_max_backlog는 이 큐의 크기, 즉 네트워크 스택에서 한 번에 처리할 수 있는 패킷의 최대 수를 설정합니다. 이 큐가 가득 찬 상태에서 추가 패킷이 도착하면, 그 패킷은 버려집니다.
  • 소켓의 연결 대기열 (somaxconn):서버가 클라이언트로부터 연결 요청을 받으면, 이 연결 요청은 일단 큐에 들어갑니다. 그 후, 서버가 준비될 때마다 큐에서 연결 요청을 꺼내와 처리(accept)합니다
  • somaxconn은 이 큐의 크기, 즉 서버가 한 번에 대기할 수 있는 연결 요청의 최대 수를 설정합니다. 이 큐가 가득 찬 상태에서 추가 연결 요청이 들어오면, 그 요청은 거절됩니다.

이렇게 backlog 네트워크의 특정 부분에서 일시적으로 데이터나 연결 요청을 보관하는 역할을 하는 대기열의 크기를 설정하는 값입니다. 서버의 용도나 트래픽의 양에 따라 backlog 값을 조절하여, 서버의 성능과 안정성을 높일 있습니다.

 

더보기

backlog는 물리적 네트워크 포트에서 패킷을 쌓아두는 커널의 큐 크기입니다. 만약 이 큐 크기가 작아서 큐에 쌓이지 못한 패킷들은 버려지게 됩니다. backlog 값을 확인 하려면, 아래의 명령어를 실행합니다. ]# sysctl net.core.netdev_max_backlog backlog는 또 한가지의 종류가 있는데 그것은 listen backlog 입니다. 클라이언트가 서버에 연결할 경우, accept 하지 못한 클라이언트들이 대기할 수 있는 최대값입니다. 만약 backlog가 1000이라면, 1000개의 클라이언트가 accept 에서 대기할 수 있습니다. listen backlog 값을 확인 하려면, 아래의 명령어를 실행합니다. ]# sysctl net.core.somaxconn 이러한 값들은 대량의 트래픽을 처리하는 서버에서는 알맞게 설정해줄 필요가 있습니다.

 

출처 : https://letitkang.tistory.com/163

Map 인터페이스는 여러 구현체를 가지고 있다.

가장 대표적인 것은 HashMap TreeMap ( 외에도 LinkedHashMap, WeakHashMap 등의 구현체) 

 

HashMap:
내부 구조 : 해시 테이블을 사용.
시간 복잡도: get과 put 연산: 평균적인 경우 O(1). 최악의 경우 (해시 충돌이 발생할 경우) O(n).
특징: 순서를 보장하지 않습니다. null 키와 null 값을 허용.

TreeMap:
내부 구조: 레드-블랙 트리 (균형 이진 검색 트리)를 사용.
시간 복잡도: get, put, remove 연산: O(log n)
특징: 키에 대해 순서를 보장합니다. null 키는 허용되지 않지만, null 값을 허용.

/*
avg 케이스가 빅오(1)이고 워스트 케이스에선 해쉬 충돌로 인해 빅오(N)이 될 수 있습니다.
Java 버전 8이전에는 해쉬 충돌에 대해 같은 Key 버켓에 대해서 LinkedList를 사용해서 빅오(N)의 시간 복잡도를 가졌지만, 
현재는 동적으로 LinkedList에서 TreeMap으로 바뀌기 때문에 빅오 로그N
*/

 

요약하자면, HashMap 해시 테이블을 기반으로 동작하여 평균적인 상황에서 get put 연산이 상수 시간에 가능하지만, 해시 충돌이 발생할 경우 성능이 저하될 있다. 반면, TreeMap 키의 순서를 보장하며, 모든 주요 연산들이 로그 시간에 이루어진다.

 

HashMap과 TreeMap의 선택은 상황과 필요에 따라 달라진다. 

HashMap은 평균적인 경우에 빠른 접근 시간 O(1)을 제공하지만, 해쉬 충돌이 자주 발생하면 성능 저하의 위험이 있음. 

반면 TreeMap은 밸런스드 트리 구조로 인해 항상 O(log n)의 시간 복잡도를 가진다.

 

데이터가 많지 않고 해쉬 충돌의 가능성이 적다면 HashMap이 더 유리
데이터가 엄청 많아지면 평균적으로 빅오(lonN)인 TreeMap이 유리

 

/*

ArrayList와 LinkedList와의 비교

ArrayList는 인덱스 기반의 접근에 최적화되어 있어 빠른 조회를 제공하지만, 중간에 요소를 삽입하거나 삭제할 경우에는 비효율적 

LinkedList는 중간에 요소를 삽입하거나 삭제할 때 효율적이지만, 인덱스 기반의 조회에서는 비효율적

*/

 

TreeMap의 워스트 케이스 시간 복잡도 :

삽입(insert), 삭제(delete), 조회(lookup): TreeMap은 레드-블랙 트리를 기반으로 구현되어 있다. 따라서 이러한 연산들의 워스트 케이스 시간 복잡도는 모두 O(log n).

레드-블랙 트리는 밸런싱을 통해 트리의 깊이가 너무 깊어지지 않도록 보장하여, 모든 연산에 대해 로그 시간 복잡도를 유지한다.

 

TreeMap은 내부적으로 레드-블랙 트리를 사용하여 구현되어 있다. 
레드-블랙 트리는 이진 탐색 트리의 한 종류로, 트리가 균형을 유지하도록 설계되어 있다. 
따라서 삽입, 삭제, 조회 연산의 시간 복잡도가 일반적으로 O(log n)으로 매우 효율적이다.

그러나 정말로 특이한 경우에, 즉 이진 탐색 트리가 완전히 한 쪽으로 치우쳐져 있을 때 
(즉, 모든 노드가 왼쪽 자식 또는 오른쪽 자식만을 가질 때) 워스트 케이스의 시간 복잡도는 O(n)이 될 수 있다. 
그러나 레드-블랙 트리의 특성 상 이런 워스트 케이스는 발생하지 않도록 트리의 균형을 유지하는 연산이 수행됨.

따라서 실제 TreeMap의 구현에서는 이러한 워스트 케이스 시나리오가 발생하지 않도록 보장되며, 
그 결과 TreeMap의 삽입, 삭제, 조회 연산의 시간 복잡도는 항상 O(log n).

어떻게 의존성을 관리하는게 좋은 설계가 되는가 ? 

설계 = 코드를 어떻게 작성할 것인가 ? , 어떤 코드, 어떤 클래스 어떤 패키지에 어떤 코드로 작성 ? 

변경의 핵심은 의존성이다, 의존성이란 변경에 의한 영향 가능성 ! 1. 클래스의존성 2. 패키지의존성 

1. 클래스 의존성

 - 연관관계(Association) : 경로상에 존재 

 - A -> B 영구적으로 갈 수 있는 경로가 있는 것 

class A {
	private B b;
}

 - 의존관계(Dependency) : 코드상에서 파라미터나 리턴 타입에 그 타입이 나오는 것, 메소드에서 그 타입의 인스턴스를 생성하는 것 

- 협력을 하는 순간 일시적으로만 관계를 맺어 주는 것 

class A {
	public B method (B b) {
    	return new B();
    }
}

- 상속관계(Inheritance) :  구현이 바뀌면 영향을 받음

class A extends B {}

- 실체화 관계(Realization) : 인터페이스 implements, 인터페이스 operation의 시그니쳐가 바뀌었을 때만 영향을 받음 

class A implements B {}

 

2. 패키지 의존성 : 패키지에 포함된 클래스 사이의 의존성

 

* 좋은 의존성을 관리하기 위한 몇 가지 규칙 

1. 양방향 의존성을 피해라 : 양방향 > 단방향 

2. 다중성이 적은 방향을 선택하라 : OneToMany -> ManyToOne , 단방향참조의 방향

Class A {
	private Collection<B> bs;
}

Class B {}

A(OneToMany) -> B 보다는

Class A {} 
Class B {
	private A a;
}

B -> A(ManyToOne) 으로 !

3. 의존성이 필요 없다면 제거하라

4. 패키지 사이의 의존성 사이클을 제거하라 (BiDir -> UniDir)

 

 

예시 ) 주문 Validation

1. 협력 설계하기 - 클래스 다이어그램(관계=의존성=객체)에는 방향성이 필요)  

2. 과계의 종류 결정하기 

 - 연관관계(탐색가능성,navigability) : 협력을 위해 필요한 영구적인 탐색 구조

 - 의존관계 : 협력을 위해 일시적으로 필요한 의존성(파라미터, 리턴타입, 지역변수)

* 두 객체 사이에 협력이 필요하고 두 객체의 관계가 영구적이라면 연관관계를 이용해 탐색 경로 구현

* 객체 참조를 이용한 연관관계 구현 

* 객체관의 관계 = 레이어 아키텍쳐(Layered Archetecture) 중 Domain 영역 !

 

설계 개선하기

1. 코드 작성 후 의존성 관점에서 설계 검토 

2. 두가지 문제 

 (1) 객체 참조로 인한 결합도 상승 :  중간 객체를 이용한 의존성 사이클 끊기 

- 객체참조로 구현한 연관관계의 문제점 : 협력을 위해 필요하지만 두 객체 사이의 결합도가 높아짐

- 성능문제 : 객체 그룹의 조회 경계가 모호, 어디까지 조회할 것인가 ? 

- 수정 시 연관된 도메인 규칙을 함께 적용할 객체는? (트랜잭션의 경계는 어디까지?, 어떤 테이블까지 하나의 단위로 lock을 설정?)

- Warning : 변경의 빈도가 다르다 -> 트랜젝션 경합으로 인한 성능 저하 

객체참조의 문제점 : 객체참조는 결합도가 가장 높은 의존성 > Repository를 통한 탐색 (약한 결합도)

 

<어떤 객체들을 묶고 어떤 객체들을 분리 ? > 
 1. 함께 생성되고 함께 삭제되는 객체들을 함께 묶어라. 

 2. 도메인 제약사항을 공유하는 객체들을 함께 묶어라

 3. 가능하면 분리하라 

 - 경계 안의 객체는 참조를 이용해 접근

 - 경계 밖의 객체는 ID 를 통해 연관관계 설정 및 접근

 - 객체의 단위 = 트랜젝션의 단위 = 조회 단위 

 - 일단 참조 없는 객체 그룹으로 나누고 나면 그룹 단위의 영속성 저장소 변경 가능 : 그룹은 트렌젝션/조회/비즈니스 제약의 단위 

 - 객체를 직접 참조하는 로직을 다른 객체로 옮기자 : 낮은 응집도의 객체로 단일책임원칙

 - 때로는 절차지향이 객체지향보다 좋다 

 - 본질 : 도메인 로직의 순차적 실행 

   1. 절차지향 로직(OrderValidator와 동일)

   2. 도메인 이벤트(DomainEvent) 퍼블리싱 : 의존성 제거   

* 추상화 ? 변화가 없을때 ! 재사용성 ! 

 (2) 패키지 의존성 사이클 

 

의존성과 시스템 분리 

도메인 단위 분리 시 의존성 사이클 존재

도메인 단위 모듈 = 시스템 분리의 기반 

* 패키지 의존성 사이클을 제거하는 3가지 방법 

1. 새로운 객체로 변환 2. 의존성 역전 3. 새로운 패키지 분리 

SystemEvent 를 통한 시스템 통합 

 

 

https://www.youtube.com/watch?v=dJ5C4qRqAgA