일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- ATDD
- 클린코드
- 부모객체
- Runtime Area
- java
- 스프링으로하는마이크로서비스구축
- Execution Engine
- 미니미프로젝트
- M:N
- G1GC
- 트랜잭션 격리 수준
- RESAPI
- DB
- Java 22
- 완벽이해
- hateoas
- docker
- testdrivendevelopment
- Spring/JAVA 서적
- 마이크로서비스디자인패턴
- 자식객체
- TDD
- Be
- pair programming
- KPT
- ComponentScan
- Self Descript Message
- 도커
- GC
- Solid
- Today
- Total
Programming Summary
Programming) OOP란? 그리고 그 외 지식들 본문
1) What OOP?
OOP란 Object-Oriented Programming의 줄임말로 객체 지향 프로그래밍을 의미한다. 대부분의 프로그램을 짜는 이유는 현실 세계의 문제를 해결하기 위해 로직을 만들게 된다. 이를 위해 여러 개의 독립된 단위, 객체들의 상호작용을 통해 프로그램 로직을 구성하여 현실 세계를 모방하였다.
2) Why OOP?
그렇다면 왜 객체 지향 프로그래밍을 해야할까?
한 파일안에 모든 코드를 넣는 것을 가정해보자. 어떤 장점이 있을까? 일단 읽기가 편하다. 다른 사람의 코드를 본 사람들은 알겠지만, 여러 파일로 되어있는 코드는 서로 추상적이게 의존하고 있고 구체적인 로직이 궁금하다면 여러 파일을 거치며 이해해야 하므로 많은 수고가 든다. 그러니 한 파일 내에 있으면 보다 가독성을 높일 수 있다. 다만 한 파일에 다 넣었을 때의 단점은 유지/보수가 매우 힘들다는 점이다. 이러한 유지/보수성은 그렇다면 왜 중요할까? 요구사항이 복잡하고 변경이 자주 되기 때문이다. 요즘 프로그램의 크기가 매우 커지다보니 간단한 기능 하나를 수정하더라도 여러 군데를 고쳐야하고, 예상치 못한 오류에 마주쳐야한다. 이렇듯, 요구사항이 변경되었을 때, 코드 변경을 최소한으로 하기 위해 OOP를 사용하게 되었다.
3) OOP 역사
객체 지향 언어는 1960년 조한 달과 크리스틴이 발표한 시뮬라67에서 첫 등장했다. 시뮬라67에서는 OOP의 가장 중요한 개념 중 하나인 클래스를 도입하여, 이후 스몰토크, C++ 등에 영감을 주었다. 하지만 1970년대 컴퓨터 산업을 주도한 IBM, 미 국방성 등의 관심을 받지 못해 실용적인 언어로 발전하지 못하였다.
이후 등장하게 된 언어는 스몰토크라는 언어이다. 스몰토크는 앨런 케이의 책임 하에 제작이 된 언어로, 가장 순수한 객체 지향 언어로 인정받고 있다. 스몰토크에서는 3가지 가정하에 제작 되었는데 1) 모든 사람들이 컴퓨터를 사용할 것이라 가정 2) 모든 사용자가 그래픽이 지원되는 모니터와 마우스를 기본 설비로 사용할 것이라 가정 3) 모든 사람이 각자의 응용 프로그램을 쉽게 개발할 것이라 가정하였다.(1,2번의 가정은 맞았으나 3번은 가정이 현실화되죈 않았다.) 즉, 스몰토크는 GUI를 만들어야 하는 언어였는데 이를 위해 모든 GUI를 객체로 구성하고, 각 객체들이 event를 통해 소통하는 Event Driven 방식으로 동작하도록 구성하였다. 살짝 구체적으로 얘기해보지면, 객체가 동작을 하게 되면 이벤트가 발생하고 이 이벤트는 이벤트큐에 저장되었다가 하나하나 이벤트 핸들러가 처리하는 방식으로 동작하게 되었다.그리고 객체와 객체는 서로 메시지를 주고받으면서 관리하는 형식으로 동작했다.
4) OOP에서는 상태보다는 행동이 중요하다!
클래스를 정의할 때를 생각해보자. 클래스에는 필드와 메소드가 있다. 필드와 메소드 중 어떤 것이 더 중요할까?
정답은 메소드이다. 이 객체가 어떠한 역할을 하는지를 결정하는 것은 메소드이기 때문이다. 우리는 필드를 만들고 이에 대한 메소드를 만드는 방식에 익숙하지만, 이는 OOP적이지 않다고 할 수 있다. 먼저 메소드를 정의하고 이에 맞는 필드를 정의하는 방식이 더 OOP스러운 방식이다.
그리고 우리는 메소드를 작성할 때 미래의 재사용성을 생각하며 작성하는 경우가 많다. 하지만 무리하게 범용성과 재사용성을 확보하면서 짜는 것보다 한 메소드가 견고하게 한가지 일을 하도록 작성하는 방식이 더 적합하다. 이러한 OOP를 지키기 위한 원칙으로 대표적인 SOLID 원칙이 있다.
5) SOLID 원칙
SOLID 원칙은 로버트 마틴이 2000년대 초반에 명명한 객체 지향 프로그래밍의 다섯가지 기본 원칙을 마이클 페더스가 두문자어 기억술로 소개한 것이다.
- S(SRP : 단일 책임의 원칙)
- 클래스는 하나의 책임을 가져야한다. 이는 조금 추상적인 말이다. 이를 조금 더 구체적으로 말해보자면, 클래스가 변경되어야 하는 이유는 하나여야 한다는 것이다. 클래스를 변경하는데 사유가 여러개면 잘못된 방식이다. 만약 한 클래스가 너무 많은 책임을 갖고 있다면 단 하나의 책임만 수행하도록 변경할 수 있다. 이를 책임 분리라고 한다. 이를 읽는 순간 다들 느꼈겠지만, '이걸 실현할 수 있나?'라는 의문점이 든다. 이는 어디까지나 원칙으로 권고사항이다. 너무 부담감을 느끼며 코드를 짜진 말자.
- O(OCP : 개방-폐쇠의 원칙)
- 확장에는 열려있고 변경에는 닫혀있어야 한다는 원칙이다. 이는 인터페이스를 통해 주로 구현된다. 이를 조금 더 구체적으로 살펴보자.
- 확장에 대해 열려 있다: 요구사항이 변경될 때 새로운 동작을 추가하여 애플리케이션의 기능을 확장할 수 있다.
- 수정에 대해 닫혀 있다: 기존의 코드를 수정하지 않고 애플리케이션의 동작을 추가하거나 변경할 수 있다.
- 자세한 것은 망나니 개발자님의 예시를 살펴보자. 이해가 잘되더라. (https://mangkyu.tistory.com/194) 이 블로그를 참조하면 OCP가 본질적으로 얘기하는 것은 추상화이며, 런타임 의존성과 컴파일타임 의존성에 관한 얘기로 연결된다고 한다. 왜냐하면 컴파일 시점에서는 추상화된 인터페이스를 의존하지만 런타임 시점에서는 구체적인 클래스를 의존하게 되기 때문이다.(Spring에서는 이를 지원하기 위해 @Autowired라는 아주 좋은 애노테이션이 있다.)
- 확장에는 열려있고 변경에는 닫혀있어야 한다는 원칙이다. 이는 인터페이스를 통해 주로 구현된다. 이를 조금 더 구체적으로 살펴보자.
- L(LSP : 리스코프 치환 원칙)
- 부모 클래스를 자식 클래스로 바꾸어 사용할 수 있어야 한다는 의미이다. 이것도 OCP에 이어 추상적인 것에 의존하라는 의미이다. 이를 조금 더 자세히 말하자면 해당 객체를 사용하는 클라이언트는 상위 타입이 하위 타입으로 변경되어도, 차이점을 인식하지 못한 채 상위 타입의 퍼블릭 인터페이스를 통해 서브 클래스를 사용할 수 있어야 한다는 것이다.
- 대표적으로 ArrayList를 들 수 있다. ArrayList를 선언할 때 부모인 List를 사용하는 게 더 좋다. 우리가 주로 사용하는 메서드들은 ArrayList만의 특성이 아니라 List 인터페이스에 정의된 메서드를 사용한다. 즉, 어떤 방식으로 구현된 List인지보다 List라는 것을 사용한다는 것이다.
- I(ISP : 인터페이스 분리 원칙)
- 객체가 충분히 높은 응집도의 작은 단위로 설계되었더라도, 목적과 관심이 각기 다른 클라이언트에 있다면 인터페이스를 통해 적절히 분리해줄 필요가 있다. 이를 인터페이스 분리 원칙이라 부른다. 조금 더 쉽게 말하자면, 인터페이스를 만들 때 너무 광범위하게, 제너럴하게 만들지 말고, 조금씩 만들어 사용하도록 하라는 의미이다. 이를 준수함으로써 모든 클라이언트가 자신의 관심에 맞는 퍼블릭 인터페이스만을 접근하여 불필요한 간섭을 최소화할 수 있으며, 기존 클라이언트에 영향을 주지 않은 채로 유연하게 객체의 기능을 확정하거나 수정할 수 있다.
- D(DIP : 의존관계 역전 원칙)
- 상위 모듈이 하위 모듈에 의존하지 않도록 구현하는 원칙이다.
- 고수준, 상위 모듈 : 입력과 출력으로부터 먼(비즈니스와 관련된) 추상화된 모듈
- 저수준, 하위 모듈 : 입력과 출력으로부터 가까운(HTTP, 데이터베이스, 캐시과 관련된) 구현 모듈
- 결국 의존관계 역전 원칙이란 비즈니스와 관련된 부분이 세부사항에는 의존하지 않도록 설계하는 것이다. 의존 역전 원칙은 개방 폐쇠 원칙과 밀접한 관련이 있으며 이를 위배하면 개방 폐쇄 원칙 역시 위배될 가능성이 높다.
- 상위 모듈이 하위 모듈에 의존하지 않도록 구현하는 원칙이다.
6) 그 외 지식들
Spring
Spring이란 독립실행이 가능한 자바 어플리케이션 프레임워크이다.(웹 개발 프레임워크가 아니다!) 이를 통해 우리는 조금 더 견고하고 쉽게 자바 어플리케이션을 만들 수 있다. 이를 조금 더 풀어 말하자면 Spring은 객체 지향 프레임워크로 좀 더 OOP 스럽게 코드를 짤 수 있도록 해준다. 다음은 Spring Triangle로 스프링의 핵심 3대 요소를 말한다.
Spring에서 핵심이 세가지 기술은 의존 관계 역전 원칙(DIP), 관점 지향 프로그래밍(AOP), 서비스 추상화(PSA)라고 할 수 있겠다. 구체적인건 밑의 링크를 참조하자.
TDD
TDD란 Test Driven Development의 줄임말로 테스트 기반 개발이다. TDD는 설계까진 아니지만 코드를 짜기 전에 많은 것을 고려하고 생각할 수 있게 해주는 방법론으로 테스트 코드의 퀄리티와 코드 퀄리티를 같이 올릴 수 있는 좋은 방법론이다. 다만 그만큼 생산성이 감소하는 것은 어쩔 수 없다.
BDD는 TDD에서 파생된 개발론으로 Behavior Driven Development, 행위 주도 개발이다. 이는 동작을 정의하는데 초점을 두고, 비즈니스 요구사항을 분석하여 개발하는데 초점을 둔 개발방식으로 Given, When, Then 순서로 테스트를 진행한다.
ATDD는 Acceptance Test Driven Development, 인수 테스트 주도 개발이다. 이는 고객, 개발자, 테스터간의 커뮤니케이션을 기반으로 하는 개발 방법론으로 고객-개발자-테서터간의 협업을 강조한다는 점에서 TDD와 많이 다르다. TDD와 비슷하게 인수 테스트를 먼저 작성하는 식으로 개발이 이루어지게 된다. 인수 테스트란 사용자 시나리오에 맞춰 수행하는 테스트로, 보통 다른 의사소통집단으로부터 시나리오를 받아 인수테스트를 진행하게 된다.(소프트웨어 인수하기 전에 명시된 상세 요구사항대로 잘 작동하는지를 확인하는 테스트라 생각하면 이해에 도움이 된다.)
그 외 지식들
- 추천 책
- 스프링 입문을 위한 자바 객체 지향의 원리와 이해 - 스프링 기초
- 객체 지향의 사실과 오해 - 비유해서 설명해줘서 괜찮은 책, 알고 보면 더 좋은 책
- 이펙티브 자바 - 말이 필요없는 책
- 초보에서 벗어날 때 등한 시 할 수 있는 주제들
- 자바 스트림, Lambda, Functional Interface
- 스레드와 동기화 / Concurrent 패키지
- 리플렉션(Reflection)
- 애너테이션(Annotation)
- IO와 NIO의 차이
- 박싱과 언박싱 / 동일성과 동질성(동등성) : ==과 equals
- a.equals(b) -> 내부의 값만 같은 객체다. (동등성)
- a == b -> 아예 똑같은 객체다. 주소값과 같은 메타 데이터까지 다 같다. (동일성)
- 다만 작은 값의 경우 JVM에서 이미 자동 객체를 만들고 이를 Runtime Constant Pool(런타임 상수 풀)에 등록하고 반환하는 형식으로 구현되어 있어서 ==을 해도 true를 반환할 때가 있다.(Interning String)
- Integer -> int : auto UnBoxing
- int -> Integer : auto Boxing
- enum
- enum은 사용자 정의 타입이다. 갯수가 유한한 우리만의 타입을 만들고 싶을 때 사용하는 것이다. int에는 수 많은 값이 들어갈 수 있지만, enum은 우리가 정의한 값 외에는 컴파일 에러를 발생시키므로 런타임 에러를 줄일 수 있다.
- 일급 컬렉션(First Class Collection)
- Collection을 Wrapping하여 그 외의 다른 멤버 변수가 없는 상태
- ex) List board = new ArrayList<>();
- 이를 통해 상태와 행위를 한 곳에서 관리하고, 컬렉션에 이름을 붙여줄 수 있어 비지니스에 종속적인 자료구조를 만들 수 있다.
- 일급 컬렉션을 만들 때는 컬렉션 값을 그대로 반환하는 기능은 두지 않고 가공된 값을 반환하거나 unmodifiableList를 사용하여 불변을 보장해야한다.
- 드미터의 법칙
- 그리스로마 신화 신의 이름(왜 이랬는지는 모르겠음)
- 객체가 있으면 직접 메시지를 던져주는 친구(직접적으로 결합되어 있는 객체만)외에 아무도 알아서는 안된다는 법칙
- 쉽게 말하면 . 두개쓰지 말라는 것(piece.getBoard.getRank와 같이)
- Getter, Setter는 최소화하고 message를 통해 전달할 수 있게 하자.
- endOfLine은 넣어주자.
- static으로 컬렉션을 만들면 static Object는 Java 8버전 이전에는 Permanent 영역(Method Area)에 저장되어 OOM이 발생할 수 있었으나 8버전 이후 static Object가 Heap에 들어가 관리되므로 GC의 대상이 되어 참조가 없어지면 지워질 수 있다.(그래도 남용하진 말자)
REF
SOLID : https://mangkyu.tistory.com/194
Spring Triangle : https://tailerbox.tistory.com/42
Permanent Area vs MetaSpace Area : https://jgrammer.tistory.com/144
JVM Runtime Area(Java7 vs Java8) : https://seunghyunson.tistory.com/23
'CS 공부' 카테고리의 다른 글
DB) 트랜잭션 격리수준이란? (1) | 2023.09.22 |
---|---|
DB) MVCC(Multi Version Concurrency Control)란? (0) | 2023.09.22 |
DB) 인덱스란? (1) | 2023.09.21 |
Java) JVM이란? (1) | 2023.09.08 |
GIT) Git이란? (0) | 2023.09.04 |