오늘 배운 것

Git

- push 해도 깃허브 페이지에 pull request 초록 버튼이 안 뜰 수도 있다

그러면 pull-request 탭 들어가서 수동으로 브랜치 다 맞춰주고 요청 넣을 것

Java

- Java에는 GOTO label 문법이 없는 대신 break [loop label 이름] 하면 goto랑 유사하게 된다.

- Scanner 작동 방식

https://st-lab.tistory.com/41

 

JAVA [자바] - 입력 뜯어보기 [Scanner, InputStream, BufferedReader]

이 글을 지금 이 시점에 써야 할까 고민을 많이 했다. 사실 자바를 그냥 다룰 줄만 아는 것에 목표를 둔다면 이 글이 무의미할 수도 있다. 그러나 자바에 대해 조금이라도 관심이 있고 더 배우고

st-lab.tistory.com

개행문자 때문에 scanner.next() 받고 나서는 항상 scanner.nextLine()을 해줘야한다.

sc.hasNextInt(...) 같이 자료형을 체크하는 기능도 제공한다.

객체지향 이모저모

- Static vs Singleton

느낀 점

금방 끝날 줄 알았던 팀 프로젝트가 테스트, 스펙에 대한 합의, 병합 과정 등등에서 꽤 오랜 시간이 걸리고 있다. 아직 협업이 익숙치 않아서 그런지 모든 프로그램을 이해해야하기도 하고 나나 팀원분들이나 다른 사람의 작업을 기다리는 경우도 많이 생겨서 협업이 쉽지 않구나라는 생각이 든다.

그래도 다들 잘하시는 분이라 배울 점이 많다는 것은 참 다행이고. 내일은 꼭 마무리 지어서 알고리즘 문제 + 편안한 주말을 맞이하길..

 

오늘 한 일

Git

- 팀원 분의 git 특강

오늘 오전에 설계 다 끝내기로 했는데 내가 또 git 오류가 나서 다시 git 공부에 들어가고 팀원분이 특강도 해주셨다.

내가 헷갈린 부분은 local과 origin(forked된 repository)의 branch를 동일하게 여겼다는 것이다.

 

내가 작업중인 branch로 원본 repo에 올라가있는 코드를 받아오기 위해서는

git fetch upstream(원본 repo의 별칭으로 내가 지정해둔 이름이다)    명령어를 통해 업데이트된 내용이 있는지 확인한다. 그 후

git merge upstream/dev(가져오고 싶은 branch 명)  으로 병합하면 된다.

 

내 작업물을 push하고 싶을 때는 add commit 후 git push origin jdh(branch명) 하면 내 forked repo에 올라가고 그러면 github 웹페이지 내에 pull request가 뜬다. wkdehdgk159/jdh에서 원본 repo/원하는 branch 인지 꼭 확인할 것.

객체지향 이모저모

- 객체지향 설계의 느낌적인 느낌

우리 호텔 예약 프로그램의 큰 설계는 이런 구조이다.  호텔(완전 주인님) - Handler(로직을 수행하는 직원들) - dao(데이터를 조회, 전달만 해주는 노예느낌). 그리고 나머지 객체들이다. 패키지로 구분하자면 domain(수동적으로 정보만 가진 친구들), handler, dao 로 구성되어 있다. 느낌적인 느낌?

느낀 점

걸출한 디자인 패턴 들에 비해서는 보잘 것 없지만 그래도 각자의 역할 + 역할의 무게에 따라 구분해놓으니 뭔가 있어보이는 느낌적인 느낌?? 좋다.

'TIL' 카테고리의 다른 글

[23.10.31]  (0) 2023.10.31
[2023.10.27]  (0) 2023.10.27
[23.10.24]  (0) 2023.10.24
[23.10.23]  (0) 2023.10.23
[23.10.20]  (1) 2023.10.20

오늘 한 일

Git

- 팀 프로젝트 구현 전 fork + pull request 작동확인

오늘 하루종일 git 공부만 했다. fork 이후 내 remote repo에서 local branch에서 어쩌구 저쩌구.. 정리해보겠다.

 

https://seungwubaek.github.io/tools/git/contributing_using_pull_request/

 

Git을 이용한 협업: Fork 부터 Pull Request 까지

Git은 쉽고 효율적인 버전 관리를 통해 커뮤니티(Github)에 공유된 Open Source 프로젝트 또는 개인 및 단체의 Private Source에 접근, 생성, 수정 할 수 있도록 하는 도구이다. 이 포스트에서는 Github에 업

seungwubaek.github.io

1. 3층 구조. 원본 repository(upstream) - forked repository(origin) - local repository(내가 작업할 branch가 있는 repo)

2. push할때는 upstream이 아닌 origin에 해줘야 진정한 의미의 (허락을 받는) pull reqeust

3. 다른 협업자로 인해 upstream에 수정사항이 생겼다면 git pull + git push origin branch1 으로 수정사항 반영

 

https://velog.io/@gth1123/git-pull-push%EC%9D%98-%EC%9D%98%EB%AF%B8

 

git pull, push와 origin의 의미

많이 사용하는 git cli는 암기해서 사용하고 있었다.git push origin 브랜치 이름git pull origin 브랜치 이름origin이 항상 들어가서 local에서 현재 내가 위치한 브랜치로 생각했었다.git push origin \[브랜치

velog.io

4. git origin의 의미는 = remote repository

git push origin [branch] = git이 push (현재 브랜치를)  origin의 [branch]로

git pull origin [branch] = git이 pull (현재 브랜치로) origin의 [branch]를

느낀 점

시간 쏟은 거에 비해선 아직도 잘 모르겠다.. fast forward는 뭐이며 FETCH_HEAD는 무엇이며. 블로그나 공식문서에서 하라는 대로 했는데 오류가 나고. 막무가내로 도전해보기 보다는 근본적인 작동원리를 더 살펴봐야겠다. 깃 특강 복습가자

'TIL' 카테고리의 다른 글

[2023.10.27]  (0) 2023.10.27
[23.10.25]  (0) 2023.10.25
[23.10.23]  (0) 2023.10.23
[23.10.20]  (1) 2023.10.20
[23.10.19]  (0) 2023.10.19

오늘 한 일

내배캠 자바 개인 과제

- 키오스크 과제 모범답안 확인

 

- 모범답안 중 클래스 뒤에 붙는 context란?

잘 정리해주신 글이 있었다.

https://pflb.tistory.com/30

 

Context가 뭐죠?

개발을 하다 보면 Context라는 용어를 자주접하게 된다.기능적으로만 이해하고 갑자기 의미가 궁금해서 찾아보았다.하단의 내용은 스택 오버플로우에 필자와 같은 질문과 그에대한 답변이다.질

pflb.tistory.com

요약하자면 context는 어떤 행위를 위한 정보들을 지칭한다. 이것은 필수적일 수도 있고 도움을 주는 선에서의 정보일 수도 있다.

java에서 제공하는 interface 중에도 context라는 친구가 있다고 한다.

시간관계 상 간략하게 읽어보니 이름과 객체를 binding하는 역할이라는데 내일 더 읽어봐야겠다.

 

- 자료구조 활용의 중요성

 

객체지향 이모저모

- 객체지향의 5가지 설계원칙

https://mangkyu.tistory.com/194

 

[OOP] 객체지향 프로그래밍의 5가지 설계 원칙, 실무 코드로 살펴보는 SOLID

이번에는 객체 지향 프로그래밍의 5가지 핵심 원칙인 SOLID에 대해 알아보고자 합니다. 실제로 애플리케이션을 개발할 때 어떻게 적용할 수 있을지 구체적인 예시를 들어 살펴보고자 합니다. 아

mangkyu.tistory.com

실무 코드로 봐서 그런지 이해는 잘 되지만 직접해본 건 아니라 막연하다. 이번 모범답안을 이 설계원칙을 지켰는지 비판적으로 바라보며 분석해야겠다.

 

알고리즘

- 백준 10026 적록색약(Gold 5, BFS)

느낀 점

모범답안을 보니 역할 분담을 잘 했다고 느꼈고 동시에 이게 최선일까?라는 의문도 동시에 들었다. 한 클래스에서 모든 로직을 구현한 나로서는 각각의 기능들마다 하나의 클래스가 존재해야 한다고 막연하게 생각했고 또 이 많은 클래스들을 패키지로도 잘 분류해야한다 생각했었다.

그러나 모범 답안을 보니 이 위대한 분은 그러지 않고도 각각의 역할을 깔끔하게 정리했다는 생각이 들면서도 App 클래스가 긴 것에 대해서는 좀 더 분할할 수 있지 않았을까라는 생각은 든다.

근데 사실 모두까기 인형이지 내 코드보다 훨씬 나으니 좋은 교보재라고 생각하고

다음 과제가 무엇일지는 모르겠으나 이번 모범답안을  살펴보면서 객체 지향에 대한 감을 더 잡았으면 좋겠다.

'TIL' 카테고리의 다른 글

[2023.10.27]  (0) 2023.10.27
[23.10.25]  (0) 2023.10.25
[23.10.24]  (0) 2023.10.24
[23.10.20]  (1) 2023.10.20
[23.10.19]  (0) 2023.10.19

출처 및 아주 좋은 링크 : https://www.tcpschool.com/java/java_modifier_accessModifier

https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

제어자

제어자는 클래스 내부의 데이터를 보호하기 위해 클래스와 클래스 멤버에 사용하는 키워드로 접근 제어자와 기타 제어자가 있다.

접근 제어자(access modifier) : (다른 클래스에서의) 접근 레벨을 제어.

기타 제어자(non-access modifier): 기타 다른 좋은 역할을 수행

 

접근 제어자

출처: 자바 공식 문서

접근 제어자는 public, protected, default(따로 접근 제어자를 선언하지 않았을 시), private 4가지가 있다.

위 표로 말끔하게 정리된다.

public

해당 클래스를 사용하는 어디에서나 접근이 가능

 

protected 

같은 클래스 + 같은 패키지 + 상속받은 자식 클래스(같은 패키지가 아니더라도) 에서 접근이 가능

 

default

같은 클래스 + 같은 패키지에서만 접근이 가능. 위 표에서는 no modifier로 된 부분으로 따로 접근 제어자를 사용하지 않으면 자동으로 적용이 된다.

 

private

같은 클래스에서만 접근이 가능.

 

알고 나면 간단하다. 이모저모를 써보자면

어떤 클래스의 private 멤버는 public 을 통해서만 접근이 가능하기 때문에 private + public 조합이 많이 쓰인다(ex. getter, setter)

접근 제어자는 한번의 선언에서 하나만 사용할 수 있다. public protected int name;는 않뒈요.

 

 

기타 제어자

기타 제어자는 이것저것 하는 친구들이다.

final, static, abstract, transient, synchronized, volatile 이렇게 있는데 앞 세가지 친구만 다루겠다. 초보라 죄송하다.

 

final

final 제어자는 말 그대로 클래스, 멤버, 메소드를 최종 버전, 즉 변경 불가능하게 만들어버린다. 

필드와 전역 변수에 사용하면 constant 상수로 만들어 버리고

메소드에 사용하면 overriding이 불가능하게 되고

클래스에 사용하면 상속을 불가능하게 만든다

static

static은 특정 멤버를 모든 객체(클래스)에서 공유하고 싶을 때 사용한다.

즉 static 제어자를 가지는 멤버는 해당 클래스의 모든 인스턴스에서 접근이 가능하며.

따로 객체를 생성(new)하지 않아도 클래스명.멤버 로 접근이 가능하다.

static 멤버는 프로그램 시작 시 단 한번만 생성되며. 메모리 영역에서(code, data, heap, stack 구조로 봤을 때) data영역에 들어간다.

또한 garbage collector의 관리도 받지 않는다.

abstract

이 친구는 추상클래스, 추상메소드를 만들 때 사용된다.

abstract 제어자를 사용한 메소드에는 실제 로직이 존재하지 않고 선언만 해준다. abstract void eat(); 이런 식으로

자식 클래스에게 어떤 메소드를 강제적으로 구현하게끔 할 때 사용하면 좋다.

 

 

보너스  부록  private + static + final

절대 변하징 않을 상수를 선언할 때 private static final ~~ 조합을 많이 볼 수 있다.

생각해보면 private final만 해도 변경 불가능 하고 든든한 접근 제어자 private도 함께하니 보안적인 측면에서 봤을 때 문제가 없어 보인다.

실제로도 보안은 탄탄하지만 static을 이용하면 프로그램 시작 시 단 한번만 사용되기 때문에 메모리적인 이득이 있다.

그냥 변하지 않을 값인데 굳이 많이 생성될 수도 있는 인스턴스의 위험부담을 안을 필요가 없다.

오늘 한 일

내배캠 자바 개인 과제

- 키오스크 과제 추가 스펙 구현 (총주문, 금액 출력 + 옵션 선택)

객체지향 이모저모

- 인터페이스 vs 추상클래스 : 둘 다 큰 분류로 묶는 것은 동일하나 추상클래스는 본질적인 포함관계, 인터페이스는 기능적인 분류로 묶는다. 번개 강의해주신 튜터님에게 감사(기도 모양).

- 부모 클래스에 private 필드를 선언하면 자식 클래스라도 접근할 수 없다. 최소한 protected로 선언해줘야 자식 클래스에서 접근 가능.

- 자바에는 내가 C++에서 많이 썼던 pair 자료구조가 없었다. 원래는 class 따로 만들어서 이용했는데 java8부터 지원되는 것을 확인

 

느낀 점

인터페이스와 추상클래스 차이점이 모호했는데 감이 잡힌 느낌이다. 주말에 이거 + 접근 제어자에 관해서 포스팅 하나 남겨야겠다.

'TIL' 카테고리의 다른 글

[2023.10.27]  (0) 2023.10.27
[23.10.25]  (0) 2023.10.25
[23.10.24]  (0) 2023.10.24
[23.10.23]  (0) 2023.10.23
[23.10.19]  (0) 2023.10.19

오늘 한 일

내배캠 자바 개인 과제 필수사항 완료

- 키오스크 과제 필수 스펙 구현 완료

- There is no default constructor available in 'parent class' 에러 해결.  부모 클래스에 생성자가 있다면 본인 생성자를 호출하기 전 부모 클래스 생성자를 호출해주어야 한다. 아래 super(name, description)이 필수.

public class Menu {
    private String name;
    private String description;
    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }
}
public class Product extends Menu{
    String name;
    String description;
    int price;

    public Product(String name, String description, int price) {
//        this.name = name;
//        this.description = description;
        //이렇게 하면 에러.
        super(name, description);
        this.price = price;
    }
}

 

 

객체지향 4대 원칙 공부

- 추상화, 상속, 다형성, 캡슐화  : 과제를 구현은 했으나 객체 지향적으로 했는지 모르겠다고 하니 고수 팀원 분이 블로그 글을 추천해주셨다.

 

느낀 점

과제를 완성하긴 했는데 Menu, Product 클래스는 거의 구조체(struct)나 다름없고 주요 로직은 한 클래스에 몰아둬서 자바스크립트와 다름이 없다. 튜터님께도 여쭤봤는데 아직 감이 안 잡힌다

과제 해설이나 정답이 나오면 공부한 4대 원칙을 유념하면서 감을 잡아봐야겠다.

'TIL' 카테고리의 다른 글

[2023.10.27]  (0) 2023.10.27
[23.10.25]  (0) 2023.10.25
[23.10.24]  (0) 2023.10.24
[23.10.23]  (0) 2023.10.23
[23.10.20]  (1) 2023.10.20

안녕하세요 오늘은 아침부터 예비군 통지서가 온 덕에 입꼬리가 내려가는 하루네요^^ 하하

단순 누적합 문제처럼 보이지만 고려할 부분이 많은 문제가 있어 하나 소개해드릴까 합니다. 그럼 시작해보죠!!

 

https://www.acmicpc.net/problem/10986

 

10986번: 나머지 합

수 N개 A1, A2, ..., AN이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오. 즉, Ai + ... + Aj (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j)

www.acmicpc.net

 

1. 문제

수 N개 A1, A2, ..., AN이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오.

즉, Ai + ... + Aj (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j) 쌍의 개수를 구해야 한다.

 (1 ≤ N ≤ 10^6, 2 ≤ M ≤ 10^3, 0 ≤ Ai ≤ 10^9)

 

2. 풀이

구간합이 M으로 나누어 떨어지는 경우의 수를 구하는 문제입니다.

가장 그리디한 풀이는 sum[i][j]라는 배열을 선언하고 모든 구간합을 구한 후 sum[i][j] % M = 0이 되는 경우의 수를 구하는 것일텐데요

골드 3 문제답게 10^6이라는 통 큰 N을 주면서 O(n^2)의 시간복잡도를 방지하였습니다.(prefix sum으로 1차원 배열을 하더라도 시간복잡도는 똑같겠네요).

핵심은 '모듈러 연산'이 0이 되는 경우의 수를 구하는 것입니다.

일단 prefix sum으로 sumarr[j] - sumarr[i-1]이 구간합이 되게끔 누적합을 모두 구해줍니다.

우리가 궁금한 것은 (sumarr[j] - sumarr[i-1]) % M = 0 이 되는 것인데

 

모듈러 연산은

(A + B) % C = (A % C + B % C) % C

(A - B) % C = (A % C - B % C) % C

가 성립합니다

 

따라서 (sumarr[j] - sumarr[i-1]) % M = 0 인 경우라면

(sumarr[j] % M - sum[i-1] % M) % M = 0이 될 것이고

저희는 sumarr[j] % M = sum[i-1] % M 인 경우의 수를 찾으면 되는 것입니다.

 

추가로 sumarr[j] % M = sum[i-1] % M 인 경우의 수는 서로 다른 두 누적합의 나머지가 같은 경우만 고려한 것이고

Ai 원소 하나로 M으로 나누어 떨어지는 경우의 수도 있으니 이 또한 추가로 더해줍니다.

 

3. 코드

//https://www.acmicpc.net/problem/10986
//10986. 나머지 합(Gold 3, 누적합)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

#define endl "\n"
#define INT_INF 2147483647
#define LL_INF 9223372036854775807
#define ll long long
using namespace std;

ll N, M, ans = 0;
ll sumarr[1000000];
//sumarr의 나머지를 세는 배열
ll marr[1000];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    freopen("input.txt", "r", stdin);

    //INPUT
    cin >> N >> M;

    //SOLVE
    //누적합 계산
    ll sum = 0;
    ll A;
    int cnt = 0;
    for(int i = 0; i < N; i++) {
        cin >> A;
        if(A % M == 0) cnt++;
        sum += A;
        sumarr[i] = sum;
    }

    //구간합 (sumarr[j] - sumarr[i-1]) & M = 0 이라면
    //(sumarr[j] % M) - (sumarr[i-1] % M) = 0 이므로 
    //(sumarr[j] % M) = (sumarr[i-1] % M)) 인 경우의 수를 찾자.
    for(int i = 0; i < N; i++) {
        marr[sumarr[i] % M]++;
    }
    for(int i = 0; i < M; i++) {
        //후보들 중 2개를 순서상관없이 고르는 경우의 수
        ans += marr[i] * (marr[i] - 1) / 2;
    }
    //marr[0]에 들어갔던 구간합은 자기 자신만으로도 해당되기 때문에 따로 더해줌
    ans += marr[0]; 

    //OUTPUT
    cout << ans;
}

 

 

4. 이모저모

개인적으로 여러번 실수했던 부분이 있습니다.

바로 나머지를 세주는 marr의 자료형을 int로 선언하여 오버플로우가 계속 발생한 것입니다.

marr를 int로 선언한 배경은 marr의 원소가 한곳에 몰려서 아무리 커봤자 N의 최대값인 10^6을 넘지 못하기 때문에

int로도 충분하다고 생각하였습니다. 

하지만 ans += marr[i] * (marr[i] - 1) / 2; 이 계산에서 int 범위를 넘기기 때문에 오버플로우가 발생한다는 사실을 백준 질문게시판을 통해 알게되었고 굳이 int를 쓰고 싶다면 저 계산식에서만

1LL * 을 더해주거나 int64_t(marr[i]) 등의 형 변환을 해주어야 한다는 것도 배웠습니다.

누적합 + 모듈러 연산의 성질 + 자료형 등등 많은 것을 배울 수 있는 문제였습니다.오늘 하루도 화이팅 하십쇼~~!!

+ Recent posts