코드

import java.util.*;
import java.util.stream.IntStream;
// n 값이 될 수 있는 연속된 자연 수 합 구하기
class 연속된자연수합 {

	public static void main(String[] args) {
		int n = 1000;
		int answer = 0, sum = 0;
		int m = n / 2 + 1; //2개 이상의 연속된 자연수의 합은 절반 정도만 필요
		int[] arr = IntStream.rangeClosed(1, m).toArray();
		
		//rt를 담당하는 외부 반복문
		for (int rt = 0,lt = 0; rt < m; rt++ ) {
			sum+= arr[rt];
			//lt를 담당하는 내부 반복문
			//n보다 sum이 크거나 같을때.
			while(n<=sum) {
				//sum이 n과 같다.
				if(sum==n) {
					prettyPrint(arr, lt, rt);
					answer++;
					sum-=arr[lt++];
				//sum이 n보다 크다.
				}else {
					sum-=arr[lt++];
				}
			}
		}
		System.out.println(answer);
	}
	static void prettyPrint(int[] arr, int lt, int rt) {
		System.out.print("[lt="+lt+" rt="+(rt)+"]");
		Arrays.stream(arr, lt, rt+1).forEach(n->System.out.print(n+","));
		System.out.println();
	}
}

결과

[lt=27 rt=51]28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,
[lt=54 rt=69]55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,
[lt=197 rt=201]198,199,200,201,202,
3

여기서 while은 특정 조건에만 반복한다. 따라서 시간복잡도는 이중반복문과는 다르다.

'자료구조&알고리즘 > 자바(Java) 알고리즘 문제풀이 : 코딩테스트 대비' 카테고리의 다른 글

모든 아나그램 찾기  (0) 2022.11.30
K번째 큰 수  (0) 2022.11.27
연속부분수열  (0) 2022.11.23
교집합  (0) 2022.11.20
두배열합치기  (0) 2022.11.17

아래 결과를 이해하는 것이 목표

SELECT DATA
     , TRANSLATE (DATA, ' 1234567890', ' ') AS "숫자만제거"
     , TRANSLATE (DATA, '1234567890'||DATA, '1234567890') AS "숫자만남기기"
     , TRANSLATE (LOWER(DATA), ' abcdefghijklmnopqrstuvwxyz' , ' ' ) AS "문자만제거"
     , TRANSLATE (LOWER(DATA), 'abcdefghijklmnopqrstuvwxyz'||DATA , 'abcdefghijklmnopqrstuvwxyz' ) AS "문자만남기기"
FROM (SELECT EMPNO||ENAME||HIREDATE AS DATA  
        FROM EMP )

 

 

DATA 숫자만제거 숫자만남기기 문자만제거 문자만남기기
7698BLAKE81/05/01 BLAKE// 7698810501 769881/05/01 blake
7782CLARK81/06/09 CLARK// 7782810609 778281/06/09 clark
7566JONES81/04/02 JONES// 7566810402 756681/04/02 jones
7902FORD81/12/03 FORD// 7902811203 790281/12/03 ford
7369SMITH80/12/17 SMITH// 7369801217 736980/12/17 smith
7499ALLEN81/02/20 ALLEN// 7499810220 749981/02/20 allen
7521WARD81/02/22 WARD// 7521810222 752181/02/22 ward
7654MARTIN81/09/28 MARTIN// 7654810928 765481/09/28 martin
7844TURNER81/09/08 TURNER// 7844810908 784481/09/08 turner
7900JAMES81/12/03 JAMES// 7900811203 790081/12/03 james
7934MILLER82/01/23 MILLER// 7934820123 793482/01/23 miller
7876ADAMS87/05/23 ADAMS// 7876870523 787687/05/23 adams
7788SCOTT87/04/19 SCOTT// 7788870419 778887/04/19 scott
1111YODA81/11/17 YODA// 1111811117 111181/11/17 yoda
7839KING81/11/17 KING// 7839811117 783981/11/17 king

 

TRANSLATE 만 이해하면 문자열 다루는 함수는 거의 다 이해한 것이나 다름이 없다.

 

 

 

 

TRANSLATE( 데이터, 검색문자 , 치환문자)

 

문제

SELECT TRANSLATE ('abcde', '1234567890', ' ')  FROM DUAL

정답은?

'abcde' 그대로 출력된다. '1234567890' 로 검색해서 일치하는 것이 없기 때문이다.

 

문제

SELECT TRANSLATE ('12345', '1234567890', '12345') A
     , TRANSLATE ('12345', '1234567890', '67890') B
  FROM dual

A,B 결과는?

여기서 중요한 것은 A도 똑같지만 특히 B가 왜 '67890'이라는 결과가 나왔는지다

 

'1234567890' 에서 '67890' 이 있으니까 '67890' 나왔다고 생각했다면 틀렸다.

SELECT TRANSLATE ('12345', '1234567890', '12345') A
     , TRANSLATE ('12345', '1234567890', '67890') B
     , TRANSLATE ('12345', '1234567890', '0000067890') C
  FROM dual

 C가 어떤 결과가 나올 것 같은가?

답은 '00000' 이다. 

즉, 앞서 A 도 '12345' 가 '12345' 가 그대로 출력된게 아니라 치환되어 '12345'가 된것으로 값이 잘 맞아 떨어져 그대로 출력하게 된 것 처럼 보인 것이다.

즉, A, B 다 치환된 결과이다.

 

 

'1234567890'  검색문자가 어떻게 치환되는지 알아보자

문제

SELECT TRANSLATE ('54321', '1234567890', '12345') A
     , TRANSLATE ('54321', '1234567890', '67890') B
     , TRANSLATE ('54321', '1234567890', '0000067890') C
  FROM dual

단순히 데이터를 '12345'에서 '54321' 로 변경했다. 결과가 어떻게 나올까?

아마도 B에서 복잡하게 느껴지는 사람이 많을 것 같다. 따라서 B를 기준으로 설명하겠다.

여기서 핵심은 검색문자와 치환문자 변경 규칙은 위치, 즉 인덱스에 정확히 대응된다는 것을 염두하자

 

1회차 TRANSLATE ('54321', '1234567890', '67890')   ===>  '04321'

데이터 첫번째 문자 '5' 를 검색문자에 같은 '5'가 존재한다. 치환문자에 같은 위치 '0' 으로 치환된다.

2회차 TRANSLATE ('04321', '1234567890', '67890')   ===>  '09321'

데이터 두번째 문자 '4' 를 검색문자에 같은 '4'가 존재한다. 치환문자에 같은 위치 '9' 으로 치환된다.

똑같이 5회차까지 진행되서 '09876' 이 나온 것이다. 

SELECT DATA
     , TRANSLATE (DATA, ' 1234567890', ' ') AS "숫자만제거"
     , TRANSLATE (DATA, '1234567890'||DATA, '1234567890') AS "숫자만남기기"
     , TRANSLATE (LOWER(DATA), ' abcdefghijklmnopqrstuvwxyz' , ' ' ) AS "문자만제거"
     , TRANSLATE (LOWER(DATA), 'abcdefghijklmnopqrstuvwxyz'||DATA , 'abcdefghijklmnopqrstuvwxyz' ) AS "문자만남기기"
FROM (SELECT EMPNO||ENAME||HIREDATE AS DATA  
        FROM EMP )

 

DATA 숫자만제거 숫자만남기기 문자만제거 문자만남기기
7698BLAKE81/05/01 BLAKE// 7698810501 769881/05/01 blake
7782CLARK81/06/09 CLARK// 7782810609 778281/06/09 clark
7566JONES81/04/02 JONES// 7566810402 756681/04/02 jones
7902FORD81/12/03 FORD// 7902811203 790281/12/03 ford
7369SMITH80/12/17 SMITH// 7369801217 736980/12/17 smith
7499ALLEN81/02/20 ALLEN// 7499810220 749981/02/20 allen
7521WARD81/02/22 WARD// 7521810222 752181/02/22 ward
7654MARTIN81/09/28 MARTIN// 7654810928 765481/09/28 martin
7844TURNER81/09/08 TURNER// 7844810908 784481/09/08 turner
7900JAMES81/12/03 JAMES// 7900811203 790081/12/03 james
7934MILLER82/01/23 MILLER// 7934820123 793482/01/23 miller
7876ADAMS87/05/23 ADAMS// 7876870523 787687/05/23 adams
7788SCOTT87/04/19 SCOTT// 7788870419 778887/04/19 scott
1111YODA81/11/17 YODA// 1111811117 111181/11/17 yoda
7839KING81/11/17 KING// 7839811117 783981/11/17 king

 

 

"숫자만제거"의 결과가 저렇게 왜 저렇게 나왔을까?

TRANSLATE (DATA, ' 1234567890', ' ')  핵심은 공백 활용과 치환할 문자가 없음을 이해하는 것이다.

검색문자 맨 앞에 ' ' 공백이 존재한다. 치환문자는 ' ' 공백만 존재한다. 

즉, 공백이 존재하면 공백으로 치환한다. 이후 검색문자 '1234567890'은 치환할 문자가 없으므로 null처리(제거)가 된다.

검색문자 이외 철자나, 특수문자는 그대로 남아 BLAKE// .... KING// 이런 결과가 나온 것이다.

 

"숫자만남기기"의 결과를 살펴보자

(DATA, '1234567890'||DATA, '1234567890')   핵심은 데이터 활용(||) 하는 것이다

원리는 이전 치환할 문자가 없다는 것을 이용한다.

검색문자에서 '1234567890'||DATA 중 '1234567890'는 치환문자 '1234567890' 대응되어 먼저 처리된다. 

"7698BLAKE81/05/01" 를 예를 들자면 치환문자  '1234567890' 0까지 처리된 직후 결과는 "숫자만제거" 결과인 "BLAKE//" 상태일 것이다. 이후 치환할 문자가 없으니 null처리 되어 숫자만 제외하고 다 null처리된 것이다.

 

문자 처리도 숫자처리와 원리는 완벽히 같다. 다만 대소문자 구분을 한다는 것을 알리기 위해 LOWER함수만 추가 썼을 뿐 모든 면에서 동일하다.

 

 

만약 당장 이해하기 어렵다면, 위 표를 보고 공식처럼 대입해서 사용하면 된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'개발 > 오라클 SQL' 카테고리의 다른 글

NULL을 다룰 때 주의할 점  (0) 2022.12.23
오라클 비밀번호 만료  (0) 2022.11.27
IN, NOT IN, EXISTS, NOT EXISTS  (0) 2022.11.10
마이바티스 null 체크  (0) 2022.09.28
정규식을 통한 Mybatis Sql Injection 처리  (0) 2022.06.29

객체지향과 인지 능력

인간은 직접적으로 지각할 있는 사물을 넘어 추상적인 사물까지도 객체로 인식할 있는 능력이 있다. 어제 주문내역과 오늘 주문내역을 구분하는 , 출금 계좌에서 동일한 입금 계좌로 동일한 금액을 이체하더라도 구분이 가능하다.

물리적인 실체는 존재하지 않더라도 인간이 쉽게 구분하고 하나의 단위로 인지할 있는 개념적인 객체의 일종이다.

 

복잡한 세상에서 인간은 단순한 객체들로 주변을 분해함으로써 세상을 이해하려고 노력한다.

, 객체란 인간이 분명하게 인지하고 구별할 있는 물리적인 또는 개념적인 경계를 지닌 어떤 이다.

 

객체지향 패러다임은 인간이 인지할 있는 다양한 객체들이 모여 현실 세계를 이루는 것처럼 소프트웨어의 세계 역시 인간이 인지할 있는 다양한 소프트웨어 객체들이 모여 이뤄져 있다는 믿음에서 출발한다.

객체지향 패더라임의 목적은 현실 세계를 모방하는 것이 아니라 현실 세계를 기반으로 새로운 세계를 창조하는 것이다. 따라서 현실 세계 객체와 소프트웨어 세계의 객체와는 전혀 다른 모습을 보이는 것이 일반적이다.

 

예로 현실세계 전등은 사람없이 껏다 킬수 없다. 소프트웨어 전등은 자동으로 가능하다.

 

객체, 그리고 이상한 나라

이상한 나라의 앨리스

앨리스 이야기로 예시를 든다. 이야기 내용을 요약하면,

작은 문을 통과하기 위해, 주변 음식을 먹으면서 몸집을 줄였다가 늘렸다 한다.

앨리스 객체

앞의 이야기는 앨리스가 겪고 있는 키의 변화에 초점을 맞추고 있다.

앨리스는 정원으로 가기 위해 자신의 상태 계속 변화시켰다.

이런 상태변화는 앨리스의 행동이다. 앨리스 행동에 따라 앨리스 상태가 변한다.

 

상태를 결정하는 것은 행동이지만 행동의 결과를 결정하는 것은 상태이다.

예를들어, 앨리스 130cm 여기에 케이크를 먹어서 150cm 커졌다. 그럼 원래 상태 130cm + 150cm

280cm 된다.

, 앨리스가 행동의 결과는 앨리스의 상태에 의존적이다.

어떤 행동의 성공 여부는 이전에 어떤 행동들이 발생했는지에 영향을 받는다는 사실도 중요하다

앨리스가 문을 성공적으로 통과하기 위해서는 문을 통과할 있을 정도로 충분히 몸을 작게 중여야 한다. 따라서 앨리스는 문을 통과하기 전에 먼저 키를 작게 줄이기 위해 안의 음료나 케이크를 먹어야 한다.

이것은 행동 간의 순서가 중요하다는 것을 의미한다. 문을 통과하는 행동이 성공하려면 음료나 케이크를 먹는 행동이 선행돼야만 한다.

 

 

객체, 그리고 소프트웨어 나라

물리적인 또는 개념적인 사물은 어떤 것이라도 객체가 있다.

인간의 인지 능력 안에서 개수를 있고, 다른 사물과 구분할 있으며, 생성 시점을 있고, 독립적인 하나의 단위로 인식할 있는 모든 사물은 객체다.

 

객체의 다양한 특성을 효과적으로 설명하기 위해선 객체를 상태(state), 행동(behavior), 식별자(identity) 지닌 실체로 보는 것이 가장 효과적이다.

 

책에서 객체를 다음과 같이 정의한다.

객체란 식별 가능한 개제 또는 사물이다. 객체는 자동차처럼 만질 수 있는 구체적인 사물일 수도 있고, 시간처럼 추상적인 개념일 수도 있다. 객체는 구별 가능한 식별자, 특징적인 행동, 변경 가능한 상태를 가진다. 소프트웨어 안에서 객체는 저장된 상태와 실행 가능한 코드를 통해 구현된다.

 

 

상태

상태가 필요한가

객체가 주변 환경과의 상호작용에 어떻게 반응하는가는 시점까지 객체에 어떤 일이 발생했느냐에 좌우된다.

예를들어 비행기를 이용하려면 탑승 전에 발권이 필수다.

자판기에 음료를 먹기위해서는 충분한 돈을 넣어야 한다.

 

공통점은 어떤 행동의 결과는 과거에 어떤 행동들이 일어났었느냐에 의존한다는 것이다.

 

일반적으로 과거에 발생한 행동이력을 통해 현재 발생한 행동의 결과를 판단하는 방식은 복잡하고 번거롭고 이해하기 어렵다.

행동과정과 결과를 단순하게 기술하기 위한 상태라는 개념을 고안했다.

비행기 탑승 가능 여부는 항공권의 발권 상태를 보고 예측할 있다.

자판기는 현재까지 투입된 금액의 상태를 기억한다.

 

상태를 이용하면 과거의 모든 행동이력을 설명하지 않고도 행동의 결과를 쉽게 예측하고 설명할 있다.

앨리스가 과거에 어떤 행동을 했었는지 모르더라도 앨리스의 키만 알면 문을 통과할 있는지 여부를 쉽게 판단할 있다.

앨리스 키와 문높이 두가지 상태만 알면 문을 통과하는 행동의 결과를 쉽게 예측할 있다.

 

 

상태와 프로퍼티

앨리스는 객체다. 앨리스의 키를 줄이거나 늘리기 위해 사용하는 음료, 케이크, 부채, 버섯 모두 객체다. 모두 뚜렷한 경계를 가지며 식별 가능하고 상태와 행동을 지니고 있다.

 

그러나 세상에 모든 것들이 객체인 것은 아니다. 앨리스의 '' 위치' 객체가 아니다. 음료의 '' 객체가 아니다. 문이 열렸는지 '여부' 객체가 아니다

, 숫자, 문자열, , 속도, 시간, 날짜, 참/거짓과 같은 단순한 값들은 객체가 아니다.

단순값들은 자체로 독립적인 의미를 가지기보다는 다른 객체의 특성을 표현하는데 사용된다.

 

때로는 단순한 값이 아니라 객체를 사용해 다른 객체의 상태를 표현해야 때가 있다.

앨리스가 음료를 들고 있는 상태를 표현하기 위해 /거짓이 아닌 음료 객체를 이용해 표현하는 것이다. 음료를 들고있나 없나는 음료객체와 연결됐나 안됐나로 구분한다.

결론적으로 모든 객체의 상태는 단순한 값과 객체의 조합으로 표현할 있다.

객체의 상태를 구성하는 모든 특징을 통틀어 객체의 프로퍼티(property)라고 한다.

앨리스 기준 ,위치,음료가 프로퍼티다.

일반적으로 프로퍼티는 변하지 않는 정적이다. 반면 프로퍼티의 값은 변할 있는 동적이다.

 

다른 시점에서 앨리스를 봤는데 음료와 연결이 없다. 음료를 버린 것으로 보인다.

시점에서 앨리스는 음료에 대한 상태를 모른다.

이처럼 객체와 객체 사이의 의미 있는 연결을 링크(link)라고 한다.

 

객체 사이 링크를 통해서만 요청을 주고 응답을 받을 있다.

, 링크를 통해서만 협력이 가능하다

 

객체 간의 선으로 표현되는 링크와 달리 객체를 구성하는 단순 값은 속성(attribute)이라고 한다.

따라서 앨리스의 키와 위치는 속성이다.

 

조합하면 프로퍼티는 단순 값인 속성과 객체와 연결을 가르키는 링크 조합이다.

 

이 책에서는 객체의 상태를 다음과 같이 정의하기로 한다.

상대는 특정 시점에 객체가 가지고 있는 정보의 집합으로 객체의 구조적 특징을 표현한다.

객체의 상태는 객체에 존재하는 정적인 프로퍼티와 동적인 프로퍼티 값으로 구성된다.

객체의 프로퍼티는 단순한 값과 다른 객체를 참조하는 링크로 구분할 수 있다.

 

객체는 자율적인 존재라는 것을 명심하라

객체지향의 세계에서 객체는 다른 객체의 상태에 직접적으로 접근할 수도, 상태를 변경할 수도 없다.

자율적인 객체는 스스로 자신의 상태를 책임져야 한다.

외부의 객체가 직접적으로 객체의 상태를 주무를 없다면 간접적으로 객체의 상태를 변경하거나 조회 있는 방법이 필요하다.

 

행동은 다른 객체로 하여금 간접적으로 객체의 상태를 변경하는 것을 가능하게 한다.

객체지향의 기본 사상은 상태와 상태를 조작하기 위한 행동을 하나의 단위로 묶는 것이라는 점을 기억하라

객체는 스스로의 행동에 의해서만 상태가 변경되는 것을 보장함으로써 객체의 자율성을 유지한다.

 

 

 

 

 

코드

import java.util.*;

// n개 == arr.length , 연속된 arr 배열 수 합이 m인 경우가 몇회인가?
class 연속부분수열 {

	public static void main(String[] args) {
		int 배열크기 = 50;
		int 합계 = 30;
		int[] arr = new Random().ints(배열크기, 1, 11).toArray();//1~10
		int answer = 0;
		System.out.println(Arrays.toString(arr));
		//핵심은 값을 증감 후 같을 수 있으니 검증로직을 한 번 더 태우는 것이다.
		for (int rt = 0, sum = 0, lt = 0; rt < arr.length;) {
			if (sum <= 합계) {
				if (sum == 합계) {
					prettyPrint(arr, lt, rt);
					answer++;
					sum -= arr[lt++];
				}
				
				sum += arr[rt++];
				
				if (sum == 합계) {
					prettyPrint(arr, lt, rt);
					answer++;
					sum -= arr[lt++];
				}
			} else {
				sum -= arr[lt++];
			}
		}
		System.out.println("총 카운트 = "+answer);
	}
	static void prettyPrint(int[] arr, int lt, int rt) {
		System.out.print("[lt="+lt+" rt="+(rt-1)+"]");
		Arrays.stream(arr, lt, rt).forEach(n->System.out.print(n+","));
		System.out.println();
	}
	
}

결과

[2, 9, 1, 3, 6, 4, 7, 4, 10, 4, 9, 10, 4, 6, 5, 2, 2, 5, 6, 3, 5, 10, 1, 8, 5, 9, 3, 8, 1, 5, 8, 6, 8, 2, 10, 8, 6, 8, 6, 1, 1, 7, 6, 5, 7, 7, 7, 9, 1, 3]
[lt=1 rt=6]9,1,3,6,4,7,
[lt=12 rt=18]4,6,5,2,2,5,6,
[lt=17 rt=22]5,6,3,5,10,1,
[lt=28 rt=33]1,5,8,6,8,2,
[lt=35 rt=40]8,6,8,6,1,1,
[lt=44 rt=47]7,7,7,9,
총 카운트 = 6

 

'자료구조&알고리즘 > 자바(Java) 알고리즘 문제풀이 : 코딩테스트 대비' 카테고리의 다른 글

K번째 큰 수  (0) 2022.11.27
연속된 자연수의 합  (0) 2022.11.25
교집합  (0) 2022.11.20
두배열합치기  (0) 2022.11.17
중복문자제거  (0) 2022.11.14

협력 속에 사는 객체

역할, 책임, 협력

실제로 협력에 참여하는 주체는 객체.

실행 중 인 어플리케이션을 보면 객체들간에 메시지를 주고받으면서 협력하는 것을 있다. 이렇듯 객체가 없으면 객체지향 세계 역시 아무런 의미가 없다. 객체지향이라 부르는 이유는 패러다임의 중심에 객체가 있기 때문이다.

 

객체는 애플리케이션의 기능을 구현하기 위해 존재한다. 작은 기능조차 객체 혼자 감당하기에는 버거울 정도로 복잡 거대하다. 때문에 객체는 다른 객체와의 협력을 통해 기능을 구현하게 된다.

따라서 협력이 얼마나 조화를 이루는지 결정하는 것은 객체다.

결국 협력의 품질을 결정하는 것은 객체의 품질이다.

 

협력 공동체의 일원으로 객체는 가지 덕문을 갖춰야 한다.

첫째, 객체는 충분히 협력적이어야 한다.

모든 것을 스스로 처리하려고 하는 전지전능한 객체는 내부적인 복잡도에 의해 자멸하고 만다

 

둘째, 객체가 충분히 자율적이어야 한다.

자기 스스로의 원칙에 따라 어떤 일을 하거나 자기 스스로를 통제하여 절제하는 것을 의미한다.

객체는 다른 객체에 명력에 복종하는 것이 아니라 요청에 응답할 뿐이다. 어떤방식으로 응답할지는 객체 스스로 판단하고 결정한다.

심지어 요청에 응할지 여부도 객체 스스로 결정할 있다.

 

객체 공동체에 속한 객체들은 공동의 목표를 달성하기 위해 협력에 참여하지만 스스로의 결정과 판단에 따라 행동하는 자율적인 존재.

 

 

상태와 행동을 함께 지닌 자율적인 객체

객체를 상태(state) 행동(behavior) 함께 지닌 실체라고 정의한다.

이 말은 객체가 협력에 참여하기 위해 어떤 행동을 한다면 행동을 하는 필요한 상태도 함께 지니고 있어야 한다는 것을 의미한다.

 

객체가 협력에 참여하는 과정 속에서 스스로 판단하고 스스로 결정하는 자율적인 존재로 남기 위해서는 필요한 행동과 상태를 함께 지니고 있어야 한다.

 

객체의 자율성은 객체의 내부와 외부를 명확하게 구분하는 것으로부터 나온다.

객체의 사적인 부분은 객체 스스로 관리하고 외부에서 일체 간섭할 없도록 차단해야 하며, 객체의 외부에서는 접근이 허락된 수단을 통해서만 객체와 의사소통해야 한다.

객체는 다른 객체가 무엇(What) 수행하는지는 있지만 어떻게(how) 수행하는지에 대해서는 없다.

 

커피를 주문하는 협력 과정에 참여한 손님과 캐시어, 바리스타는 외부의 간섭을 받지 않고 스스로 생각하고 스스로 판단하는 자율적인 존재였다.

객체의 관점에서 자율성이란 자신의 상태를 직접 관리하고 상태를 기반으로 스스로 판단하고 행동할 있음을 의미한다.

객체는 행동을 위해 필요한 상태를 포함하는 동시에(바리스타는 커피 제조 방법을 기억하고 있다) 특정한 행동을 수행하는 방법을 스스로 결정할 있어야 한다.(바리스타는 자신이 알고 있는 방법에 따라 커피를 제조한다)

따라서 객체는 상태와 행위를 하나의 단위로 묶은 자율적인 존재다.

 

과거 전통적인 개발 방법은 데이터와 프로세스를 엄격하게 구분한다

이에 반해 객체지향에서는 데이터와 프로세스를 객체라는 하나의 안에 함께 묶어 놓음으로써 객체의 자율성을 보장한다.

자율적인 객체로 구성된 공동체는 유지보수가 쉽고 재사용이 용이한 시스템을 구축할 있는 가능성을 제시한다.

 

 

 

협력과 메시지

커피를 주문하기 위해 협력하는 사람들은 자신의 책임을 다하기 위해 다른 사람들에게 도움을 요청한다.

다양한 요청 방법이 있을 것이다.

 

객체지향의 세계에서는 오직 가지 의사소통 수단만이 존재한다. 이를 메시지라고 한다.

객체가 다른 객체에게 요청하는 것을 메시지를 전송한다고 다른 객체로부터 요청을 받는 것을 메시지를 수신한다고 말한다.

결과적으로 객체는 협력을 위해 다른 객체에게 메시지를 전송하고 다른 객체로부터 메시지를 수신한다.

 

 

메서드와 자율성

객체는 다른 객체와 협력하기 위해 메시지를 전송한다.

수신자는 먼저 수신된 메시지를 이해할 있는지 여부를 판단한 미리 정해진 자신만의 방법에 따라 메시지를 처리한다. 이처럼 체가 수신된 메시지를 처리하는 방법을 메서드라고 부른다.

 

객체지향 프로그래밍 언어에서 메서드는 클래스 안에 포함된 함수 또는 프로시저를 통해 구현된다.

다른 프로그래밍 언어와 객체지향 프로그래밍 언어를 구분 짓는 핵심적인 특징 하나다.

 

메시지(요청) 메서드의 분리는 객체의 협력에 참여하는 객체들 간의 자율성을 증진시킨다.

커피를 주문하는 협력과정에서 커피 제조를 요청받은 바리스타는 커피머신, 수제 커피 등등 제조 방법을 자율적으로 선택할 있다.

 

객체지향에 대입하면 커피 주문이 요청, 커피 제작하는 방법은방법은 메서드다

커피 제조를 요청한 사람은 어떻게 커피를 제조하는지 구체적인 방법을 모른다.(자율성 보장)

 

외부의 요청이 무엇인지 표현하는 메시지와 요청을 처리하기 위한 구체적인 방법인 메서드를 분리하는 것은 객체의 자율성을 높이는 핵심 메커니즘이다. 이것은 캡슐화라는 개념과도 깊이 관련돼 있다.

 

 

 

 

객체지향의 본질

객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법

자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.

객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.

객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다.

 

 

객체를 지향하라

클래스가 객체지향 프로그래밍 언어의 관점에서 매우 중요한 구성요소인 것은 분명하다

하지만 객체지향의 핵심을 이루는 중심 개념이라고 말하긴 무리가 있다.

자바스크립트 같은 프로토타입 기반의 객체지향 언어에서는 클래스가 존재하지 않으며 오직 객체만이 존재한다. 프로토타입 기반의 객체지향객체지향 언어에서는 상속 역시 클래스가 아닌 객체 간의 위임 메커니즘을 기반으로 한다.

지나치게 클래스를 강조하는 프로그래밍 언어적인 관점은 객체의 캡슐화를 저해하고 클래스를 서로 강하게 결합시킨다.

 

객체지향 설계의 관점을 코드를 담는 클래스의 관점에서 메시지를 주고받는 객체의 관점으로 변환해야 한다.

중요한 것은 어떤 클래스가 필요한가 가 아니라 어떤 객체들이 어떤 메시지를 주고받으며 협력하는 가다. 클래스는 객체들의 협력 관계를 코드로 옮기는 도구에 불과하다.

 

클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하라. 객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다.

 

코드

import java.util.*;
class 교집합_ {	

	public static void main(String[] args){
		ArrayList<Integer> answer = new ArrayList<>();
		ArrayList<Integer> answer2 = new ArrayList<>();
		int [] a = new Random().ints(20, 0, 100).sorted().toArray();
		int [] b = new Random().ints(20, 0, 100).sorted().toArray();
		
		int p1=0,p2=0,count1=0,count2=0;
		while(p1<a.length&&p2<b.length) {
			if(a[p1]==b[p2]) {
				answer.add(a[p1]);
				p1++;
				p2++;
			}else if(a[p1]<b[p2]) {
				p1++;
			}else {
				p2++;
			}
			count1++;
		}
		
		
		for(int i=0;i<a.length;i++) {
			for(int j=0;j<b.length;j++) {
				count2++;
				if(a[i]==b[j]) {
					answer2.add(a[i]);
					break;
				}
			}
		}
		
		System.out.println(Arrays.toString(a));
		System.out.println(Arrays.toString(b));
		
		System.out.println(answer);
		System.out.println(answer2);
		
		System.out.println(count1);
		System.out.println(count2);
		
	}
}

결과

[1, 7, 10, 10, 13, 22, 26, 31, 32, 34, 44, 48, 57, 57, 66, 69, 79, 82, 88, 92]
[2, 6, 8, 19, 31, 33, 35, 40, 49, 59, 60, 60, 61, 63, 65, 73, 78, 88, 90, 99]
[31, 88]
[31, 88]
37
383

 


반복 횟수 차이가 극명하다....

 
 

가져오기 java.util.*;
교집합_ {

공개 정적 무효 메인(문자열[] 인수){
ArrayList<정수> 답변 = 새로운 ArrayList<>();
ArrayList<정수> answer2 = 새로운 ArrayList<>();
int [] a = 새로운 Random().ints(20, 0, 100).sorted().toArray();
int [] b = 새로운 Random().ints(20, 0, 100).sorted().toArray();

정수 p1=0,p2=0,count1=0,count2=0;
동안(p1<a.length&&p2<b.length) {
if(a[p1]==b[p2]) {
답변.add(a[p1]);
p1++;
p2++;
} else if(a[p1]<b[p2]) {
p1++;
}또 다른 {
p2++;
}
카운트1++;
}


for(int i=0;i<a.length;i++) {
for(int j=0;j<b.length;j++) {
카운트2++;
if(a[i]==b[j]) {
answer2.add(a[i]);
부서지다;
}
}
}

System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(b));

System.out.println(답변);
System.out.println(답변2);

System.out.println(count1);
System.out.println(count2);

}
}

 

'자료구조&알고리즘 > 자바(Java) 알고리즘 문제풀이 : 코딩테스트 대비' 카테고리의 다른 글

연속된 자연수의 합  (0) 2022.11.25
연속부분수열  (0) 2022.11.23
두배열합치기  (0) 2022.11.17
중복문자제거  (0) 2022.11.14
격자판 최대합  (0) 2022.11.12

객체지향이란 실세계를 직접적이고 직관적으로 모델링할 있는 패러다임

현실 존재하는 사물을 최대한 유사하게 모방해 소프트웨어 내부로 옮겨오는 작업

이과정에서 객체란 현실 세계에 존재하는 사물에 대한 추상화라는

 

설명은 객체지향의 기반을 이루는 철학적인 개념을 설명하는 데는 적합하지만

유연하고 실용적인 관점에선 적합하지 않다.

완벽히 일치하는 경우가 거의 없기 때문이다. 예를 들어 소프트웨어 방화벽이 건물의 방화벽과  얼마나 유사하나?

 

 

 

협력하는 사람들

커피 공화국의 아침

역할, 책임, 협력 우리가 삶을 영위하기 위해 다른 사람과 접촉하는 모든 곳에 존재한다.

각각 맡은 역할을 수행하기 위해 협력하는 과정에서 책임을 다한다.

 

하나의 커피를 주문하는 과정을 예를 들어보자

손님, 캐시어, 바리스타는 각각의 역할이 있다. 손님은 커피를 주문하는 역할, 캐시어는 커피를 주문받는 역할, 바리스타는 커피를 제조하는 역할이 있다. 역할을 책임있게 수행하는 과정에서 암묵적인 협력관계가 있다.

커피 주문이라는 협력에 참여하는 모든 사람들은 커피가 정확하게 주문되고 주문된 커피가 손님에게 정확하게 전달될 있도록 맡은 역할과 책임을 다하고 있는 것이다.

 

요청과 응답으로 구성된 협력

일상에서 발생하는 대부분의 문제는 개인 혼자만의 힘으로 해결하기 버거울 정도로 복잡하다.

때문에 사람들은 스스로 해결하지 못하는 문제와 마주치면 문제 해결에 필요한 지식을 알고 있거나 서비스를 제공해줄 있는 사람에게 도움을 요청(Request)한다.

 

일반적으로 하나의 문제를 해결하기 위해 다수의 사람 혹은 역할이 필요

때문에대한 요청이 다른 사람에 대한 요청을 유발하는 것이 일반적이다.

따라서 요청은 연쇄적으로 발생한다.(협력이라 표현)

요청을 받은 사람은 주어진 책임을 다하면서 필요한 지식이나 서비스를 제공

, 다른 사람의 요청에 응답을 한다.

요청은 연쇄적으로 발생하므로 응답 역시 연쇄적으로 전달된다.

요청과 응답을 통해 다른 사람과 협력할 있는 능력은 거대하고 복잡한 문제를 해결할 있는 공동체를 형성할 있게 만든다.

협력의 성공은 특정한 역할을 맡은 개인이 얼마나 요청을 성실히 이행하는가에 달려 있다.

 

역할과 책임

사람들은 다른 사람과 협력하는 과정 속에서 특정한 역할(role) 부여받는다.

 

역할은 어떤 협력에 참여하는 특정한 사람이 협력 안에서 차지하는 책임이나 임무를 의미한다.

 

역할은 책임이라는 개념을 내포한다.

선생님 역할은 학생을 가르칠 책임이 있음을 암시한다.

경찰관 역할은 범죄자를 잡을 책임이 있음을 암시한다.

 

특정한 역할은 특정한 책임을 암시한다.

 

사람들이 협력을 위해 특정한 역할을 맡고 역할에 적합한 책임을 수행한다는 사실은 몇 가지 중요한 개념을 제시한다.

 

여러 사람이 동일한 역할을 수행할 있다.

손님 입장에서 자신이 주문한 커피를 마실 수만 있다면 어떤 캐시어가 주문을 받는지 중요치 않다.

 

역할은 대체 가능성을 의미한다.

손님 입장에서 캐시어는 대체가능하다.(substitutable)

좀 더 정확히는 두명이 동일한 역할을 수행할 수 있다면 요청자 입장에서 어떤 사람이 역할을 수행하더라도 문제가 되지 않는다.

 

책임을 수행하는 방법은 자율적으로 선택할 있다.

커피 제조를 요청받은 바리스타는 자신만의 독특한 방법으로 커피를 제조할 있다.

중요한 것은 커피를 제조하라는 동일한 요청을 받더라도 바리스타의 역할을 수행하는 사람들마다 서로 다른 방식으로 요청을 처리할 있다는 것이다.

이처럼 동일한 요청에 대해 서로 다른 방식으로 응답할 있는 능력을 다형성이라고 한다.

 

사람이 동시에 여러 역할을 수행할 있다.

캐시어와 바리스타라는 개별적인 역할을 이용해 협력 관계를 묘사했지만

사람이 캐시어와 바리스타의 역할을 동시에 수행하는 것도 가능하다.

따라서 사람이 동시에 이상의 역할을 수행하는 것도 가능하다.

 

남자가 있다. 남자의 역할은 남편, 아이의 아버지, 직장인...

 

 

 

역할, 책임, 협력

기능을 구현하기 위해 협력하는 객체들

실세계의 커피를 주문하는 과정은 객체지향의 핵심적으로 중요한 개념을 거의 대부분 포함하고 있다.

 

사람을 객체로 , 에이전트의 요청을 메시지로, 에이전트가 요청을 처리하는 방법을 메서드로

바꾸면 대부분의 설명을 객체지향이라는 문맥으로 옮겨올 있다.

 

이제 커피 주문으로 알아본 역할, 책임, 협력의 개념을 객체지향 문맥으로 옮겨 보자

 

 

역할과 책임을 수행하며 협력하는 객체들

협력의 핵심은 특정한 책임을 수행하는 역할들 간의 연쇄적인 요청과 응답을 통해 목표를 달성한다는

일상생활에서 목표는 사람들의 협력을 통해 달성되며, 목표는 작은 책임으로 분할되고 책임을 수행할 있는 적절한 역할을 가진 사람에 의해 수행된다.

협력에 참여하는 개인은 책임을 수행하기 위해 다른 사람에게 도움을 요청하기도 하며, 이를 통해 연쇄적인 요청과 응답으로 구성되는 협력 관계가 완성된다.

객체도 이런 인간 세계와 유사하다.

 

애플리케이션의 기능은 작은 책임으로 분할되고 책임은 적절한 역할을 수행할 있는 객체에 의해 수행된다.

객체는 자신의 책임을 수행하는 도중 다른 객체에게 도움을 요청하기도 한다.

시스템은 역할과 책임을 수행하는 객체로 분할되고 시스템의 기능은 객체 간에 연쇄적인 요청과 응답의 흐름으로 구성된 협력으로 구현된다.

 

객체지향 설계는 적절한 객체에게 적절한 책임을 할당하는 것에서 시작된다.

책임이 불분명한 객체는 애플리케이션의 미래 역시 불분명하게 만든다.

 

역할은 관련성 높은 책임의 집합이다.

 

역할은 유연하고 재사용 가능한 협력 관계를 구축하는데 중요한 설계 요소다.

대체 가능한 역할과 책임 객체지향 패러다임의 중요한 기반을 제공하는 다형성과도 깊이 연관돼있다.

 

 

코드

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.stream.IntStream;

public class 두배열합치기_ {
	public static void main(String[] args) {
		
		int[] is1 = new Random().ints((int)(Math.random()*5000), Integer.MIN_VALUE, Integer.MAX_VALUE)
					.toArray();
		int[] is2 = new Random().ints((int)(Math.random()*5000), Integer.MIN_VALUE, Integer.MAX_VALUE)
					.toArray();
		
		int[] copyOf1 = Arrays.copyOf(is1, is1.length);
		int[] copyOf2 = Arrays.copyOf(is2, is2.length);
		
		
		StopWatch sw = new StopWatch();
		
		ArrayList<Integer> list1 = new ArrayList<Integer>();
		int p1=0,p2=0;
		
		sw.start();
		Arrays.sort(is1);
		Arrays.sort(is2);
		
		while(p1<is1.length && p2<is2.length) {
			if(is1[p1]<is2[p2] ) {
				list1.add(is1[p1++]);
			}else {
				list1.add(is2[p2++]);
			}
		}
		while(p1<is1.length) {
			list1.add(is1[p1++]);
		}
		while(p2<is2.length) {
			list1.add(is2[p2++]);
		}
		sw.stop();
		System.out.println("소요시간 : " + sw.time());
		
		sw.start();
		
		IntStream.concat(IntStream.of(copyOf1), IntStream.of(copyOf2))
		         .sorted()
		         .toArray();
		sw.stop();
		
		System.out.println("소요시간 : " + sw.time());
		
	}
}

class StopWatch{
	long startTime;
	long endTime;
	
	
	public long start() {
		return this.startTime = System.nanoTime();
	}
	
	public long stop() {
		return this.endTime = System.nanoTime();
	}
	
	public long time() {
		if(startTime == 0 && endTime == 0) {
			return -1;
		}
		return endTime - startTime;
	}
	
}

결과

소요시간 : 1521800
소요시간 : 1803400

1번째는 두포인터를 사용한 배열합치기(문제의도) , 2 번째는 궁금해서 해봤다.

 

두배열을 합친 결과를 정렬해야한다고 가정하고, 

소요시간이 엎치락 뒤치락한다...

 

데이터 건 수가 많으면 앞도적으로 스트림이 빠르다.

'자료구조&알고리즘 > 자바(Java) 알고리즘 문제풀이 : 코딩테스트 대비' 카테고리의 다른 글

연속부분수열  (0) 2022.11.23
교집합  (0) 2022.11.20
중복문자제거  (0) 2022.11.14
격자판 최대합  (0) 2022.11.12
숫자뒤집기  (0) 2022.11.10

14장 점진적인 개선, 15 JUnit들여다보기,16 SerialDate 리팩터링은 이전 내용을 기반으로한 예제 위주 챕터이다. 

따라서 책으로 직접봐야한다.

 

17장 냄새와 휴리스틱의 소주제를 나열하는 것으로 책 요약을 마무리하겠다.

주석

C1 : 부적절한 정보

C2 : 쓸모 없는 주석

C3 : 중복된 주석

C4 : 성의 없는 주석

C5 : 주석 처리된 코드

환경

E1 : 여러 단계로 빌드해야 한다

E2 : 여러 단계로 테스트해야 한다

함수

F1 : 너무 많은 인수

F2 : 출력 인수

F3 : 플래그 인수

F4 : 죽은 함수

일반

G1 : 한 소스 파일에 여러 언어를 사용한다.

G2 : 당연한 동작을 구현하지 않는다.

G3 : 경계를 올바로 처리하지 않는다.

G4 : 안전 절차 무시

G5 : 중복

G6 : 추상화 수준이 올바르지 못하다

G7 : 기초 클래스가 파생 클래스에 의존한다

G8 : 과도한 정보

G9 : 죽은 코드

G10 : 수직 분리

G11 : 일관성 부족

G12 : 잡동사니

G13 : 인위적 결합

G14 : 기능 욕심

G15 : 선택자 인수

G16 : 모호한 의도

G17 : 잘못 지운 책임

G18 : 부적절한 static 함수

G19 : 서술적 변수

G20 : 이름과 기능이 일치하는 함수

G21 : 알고리즘을 이해하라

G22 : 논리적 의존성은 물리적으로 드러내라

G23 : If/Else 혹은 Switch/Case 문보다 다형성을 사용하라

G24 : 표준 표기법을 따르라

G25 : 매직 숫자는 명명된 상수로 교체하라

G26 : 정확하라

G27 : 관례보다 구조를 사용하라

G28 : 조건을 캡슐화하라

G29 : 부정 조건은 피하라

G30 : 함수는 한 가지만 해야 한다

G31 : 숨겨진 시간적인 결합

G32 : 일관성을 유지하라

G33 : 경계 조건을 캡슐화하라

G34 : 함수는 추상화 수준을 한 단계만 내려가야 한다

G35 : 설정 정보는 최상위 단계에 둬라

G36 : 추이적 탐색을 피하라

자바

J1 : 긴 import 목록을 피하고 와일드카드를 사용하라

J2 : 상수는 상속하지 않는다

J3 : 상수 대 Enum

이름

N1 : 서술적인 이름을 사용하라

N2 : 적절한 추상화 수준에서 이름을 선택하라

N3 : 가능하다면 표준 명명법을 사용하라

N4 : 명확한 이름

N5 : 긴 범위는 긴 이름을 사용하라

N6 : 인코딩을 피하라

N7 : 이름으로 부수 효과를 설명하라

테스트

T1 : 불충분한 테스트

T2 : 커버리지 도구를 사용하라!

T3 : 사소한 테스트를 건너뛰지 마라

T4 : 무시한 테스트는 모호함을 뜻한다

T5 : 경계 조건을 테스트하라

T6 : 버그 주변은 철저히 테스트하라

T7 : 실패 패턴을 살펴라

T8 : 테스트 커버리지 패턴을 살펴라

T9 : 테스트는 빨라야 한다.

 

 

 

'IT책, 강의 > 클린코드(Clean Code)' 카테고리의 다른 글

1장 깨끗한 코드  (0) 2023.11.13
다시 시작  (0) 2023.11.10
13장 동시성 - 2  (1) 2022.11.12
13장 동시성 - 1  (0) 2022.11.08
12장 창발성  (0) 2022.11.05

코드

import java.util.Arrays;

public class 중복문자제거_ {
    public static void main(String[] args) {
        String value = "112233112ㅁㄴㅇㅁㄴㅇ2334551123";
        String answer = "";
        
        int len = value.length();
        for(int i = 0 ; i<len ;i++) {
            //indexOf 는 가장 앞에 존재하는 문자를 반환한다.
            //charAt 은 특정 위치 char를 반환한다. 
            //따라서 아래 조건문은 첫 번째 존재하는 문자와 인덱스 위치만 참이된다.
            if(value.indexOf( value.charAt(i)) == i )  {
                answer += value.charAt(i);
            }
        }
        System.out.println(answer);
        
        Character[] array = value.chars()
                                 .distinct()
                                 .mapToObj(num->((char)num))
                                 .toArray(Character[]::new);
        System.out.println(Arrays.toString(array));
        
    }
}

결과

123ㅁㄴㅇ45
[1, 2, 3, ㅁ, ㄴ, ㅇ, 4, 5]

중복 문자 제거는 indexOf 메서드 동작방식에 의존한다.

indexOf()는 주어진 문자에 해당하는 가장 첫 번째 위치 인덱스를 리턴한다.

즉, 대상이 첫 번째 위치로 특정되어 있다. 

이를 활용해 반환된 인덱스와 배열을 순회하는 인덱스가 같으면 중복을 제거할 수 있다.

 

 

'자료구조&알고리즘 > 자바(Java) 알고리즘 문제풀이 : 코딩테스트 대비' 카테고리의 다른 글

교집합  (0) 2022.11.20
두배열합치기  (0) 2022.11.17
격자판 최대합  (0) 2022.11.12
숫자뒤집기  (0) 2022.11.10
봉우리  (0) 2022.11.09

+ Recent posts