오늘의 커리큘럼에서는 객체지향은 어떻게 해서 생겨나게 되었는지, 그리고 객체지향의 특징은 무엇인지 객체의 연관관계를 표현하는 UML과 객체지향의 설계를 잘하는 방법에 대해서 배우게 되었다.
객체지향 프로그래밍
객체지향 프로그래밍은 객체의 행동을 기반으로 책임과 역할을 분배하여 객체간의 협력으로써 프로그램을 구현하는 방식이다. 객체지향이 등장하게 된 배경은 다음과 같다. 초기에 프로그램들은 크기가 크지 않았기에 문제가 없었지만 시간이 지남에 따라 하드웨어의 성능도 좋아지고 고객들의 요구사항은 점점 더 늘어나게 되면서 기존의 절차지향 방법대로 유지보수를 하는데에는 큰 무리가 있었다.
개발자들은 이러한 유지보수 하기 힘든 매커니즘에서 벗어나 새로운 방식으로 유지보수를 하기 쉬운 방향으로 큰 프로그램을 만드는 방향에 대해서 논의를 하기 시작했고, "행위를 수행하는 것을 작게 나눠서 만들고 합치면 되지 않을까?" 라는 생각으로 행위를 객체들에게 나눠서 수행하도록 하였다. 바로 이러한 매커니즘에서 탄생한 것이 객체지향 프로그래밍이고, 이는 프로그래밍의 새로운 패러다임을 열게 되었다.
객체란
그럼 객체란 무엇인가. 객체는 상태와 행위를 가지고 있는 어떠한 개념적인 용어이다. 종종 클래스를 객체로 오해하는 케이스를 볼 수 있는데, 클래스는 객체를 표현하기 위한 정적 코드를 작성하는 도구. 그 이상 그 이하도 아니다. 실제로도 자바스크립트 같은 경우에 최근에는 클래스를 선언할 수 있게 되었지만, 이전에는 프로토타입을 이용해서 객체지향적인 개발이 가능했다.
그럼 객체란 어떠한 일을 수행하는가? 위에서 얘기했지만 객체는 어떠한 작은 기능을 수행해야 하고, 객체는 협력으로 이루어져 있다. 이러한 협력은 객체의 행동을 기반으로 협력을 할 수 있게 된다. 따라서 객체지향적인 개발을 하기 위해서는 객체의 책임을 잘게 쪼개고, 서로 협력하도록 하는 것이 중요하다.
객체들을 서로 다른 책임을 가지고 행동을 수행한다. 따라서, 객체는 서로 구분 될 필요가 있다. 따라서 이를 구분하기 위한 방법이 필요한데 우리는 이를 Type(형) 이라고 부른다. 이러한 타입은 String, Integer, Object, Custom 등으로 다양하게 구성 할 수 있다.
객체지향의 특성
우리는 객체지향의 특성이라는 용어를 자주 들어왔다. 그리고 대부분 이를 캡상다추 라고 외우기도 한다. 그렇다면 캡상다추의 캡슐화, 상속, 다형성, 추상화가 무엇인지에 대해 알아보자.
캡슐화
먼저, 캡슐화란 보통 객체의 상태를 밖에서 접근하지 못하게 하는 정보 은닉의 목적을 가지고 있다. 하지만 우리는 종종 이러한 케이스를 보게 된다.
public class Person {
private Heart heart;
Person() {
heart = new Heart();
}
public Heart getHeart() {
return heart;
}
}
상태를 private로 감추고 Getter를 통해서 외부에서 접근이 가능하도록 하고 있다. 상태를 메서드로 감추었기때문에, 이를 캡슐화라고 생각할 수 있다. 하지만 이는 잘못된 지식이다. 이는 외부에서 p.getHeart().remove(); 처럼 잘못 접근해서 객체 자체의 기능을 할 수 없도록 만든다. 캡슐화의 근본적인 원리는 다시 한 번 말한다. 외부에서 객체의 상태에 접근하지 못하도록 막는 것이다.
따라서 캡슐화는 정보은닉 말고도 더 중요한 완성도 적인 측면이 존재한다. 즉, 기능을 수행하는 단위로써 완전함을 가져야 한다는 것이다.
상속
먼저 상속에 기본적인 용어부터 알아보자. 보통 상속관계에 대해서 얘기 할 때는, 부모와 자식의 관계로 얘기하기도 하고, 상위 객체와 하위 객체, Super Type 과 Sub Type 이렇게 구분을 하곤 한다. 하지만 이는 용어적인 측면에서의 얘기이고 제일 중요한것은 부모 즉, 상위 객체는 추상 객체라는 것이고, 자식 객체는 구체 객체 혹은 구상 객체 라고 부르기도 한다.
우리는 보통 상속이라는 개념을 처음 배우게 될 때, 메서드를 재사용하기 위함이라고 배운다. 하지만 이는 틀렸다고 말하긴 뭐하지만, 상속은 재사용을 목적으로 사용하는 개념이 아니다. 상속은 추상화와 구체화의 관점에서 바라보아야 한다. 생물 -> 동물 -> 사람 이런 관계를 만들기 위해서 상속을 사용하는 것이다. 이를 객체지향 관련된 책에서는 서브 타이핑이라고 한다. 참고로 단순히 코드를 재사용하기 위해 상속하는 말을 서브 클래싱이라고 한다. 그럼 앞에서 얘기한 추상화와 구체화는 무엇을 의미하는 것일까?
추상화
우리는 추상화에 대한 얘기를 종종하곤 한다. 뭔가를 좀 더 추상적으로 생각해봐! 뭔가 굉장히 간단해보이면서도 정말 쉽지 않은 일이다. 이런 것처럼 객체에서의 추상화는 위로 올라갈수록 어떠한 행동이 좀 더 추상적인 행동으로 변하게 된다. 예를 들어서, Door 라는 객체가 존재한다고 가정하자. 그리고 최근에는 여러가지 기능을 가진 문들이 존재한다. 지문인식기능이 있는 문, 홍채인식기능이 있는 문등 다양한 문이 존재하는데, 기존에 우리가 알고 있는 아날로그 적인 문은 사람이 힘을 가하여 문을 여는 것으로 마무리가 되지만, 저런 고급 기술들이 있는 문의 경우에는 전기선 같은 것도 필요할 것이고, 지문을 저장하는 메모리도 필요할 것이고, 다양한 것들이 집합하여 행동을 이룬다.
객체지향의 세계에서도 동일하다. 상속 관계에서는 위로 갈수록 추상화가 된 상태가 되고, 아래로 갈수록 구체화가 된다. 따라서 추상 객체는 구상 객체의 퍼블릭 인터페이스(메서드) 보다 그 수가 적거나 동일하다.
다형성
마지막으로 다형성에 대해서 얘기를 한다. 다형성이란 말 그대로 여러가지 형태를 가질 수 있음을 의미한다. 위에서 잠깐 얘기한 Door를 예시로 살펴보자.
public interface Door {
void open();
}
public class AnalogDoor implements Door {
void open(){
System.out.println("문이 열렸습니다.");
}
}
public class DoorWithEye implements Door {
Memory memory;
void open(){
System.out.println("문이 열렸습니다.");
}
void open(Eye eye) {
if(memory.contains(eye)) {
System.out.println("문이 열렸습니다.");
}
}
}
Door라는 인터페이스를 가지고, 홍채 인식 기능이 있는 DoorWithEye 클래스를 만들었다. 그리고 다형성이란 추상 타입의 클래스가 구상 타입의 인스턴스를 여러 형태로 가질 수 있는 것을 의미한다. 즉 다음과 같은 코드 형식이 가능하다는 것이다.
Door door = new AnalogDoor();
Door eyeDoor = new DoorWithEye();
그리고 두 객체는 Door의 타입을 가지고 있기 때문에 Door의 원래 행위는 open() 이라는 행위를 그대로 수행 할 수 있다. 이러한 것을 다형성이라고 한다.
이어서 UML에 대해서 다루었는데, UML에 관한 포스팅은 다음에서 확인이 가능하다.
객체지향의 설계
어쨋든 우리는 객체지향적으로 개발하기 위해서는 객체지향적인 설계가 무엇인지 이해 할 필요가 있다. 객체지향적인 설계를 잘하기 위해서는 먼저 도메인을 적절히 이해 할 필요가 있다. 그리고 해당 도메인들을 객체로 설정하고, 객체에게 행위를 부여하고, 그 행위를 수행하는데 있어서 다른 객체가 필요하다면 새로이 생성해서 그 객체와의 협력 관계를 이루도록 할 수 있다.
이러한 개념적인 것 말고도 객체지향 설계를 하는 5가지 원칙이 존재한다.
객체지향 설계의 5가지 원칙
1. 단일 책임 원칙 (Single Responsibility principle, SRP)
- 한 객체는 하나의 책임만 가져야 한다.
2. 개방 폐쇄 원칙 (Open/closed principle, OCP)
- 수정에는 닫히고, 확장에는 열어라. 객체 내부를 수정하는 것이 아니고 외부에서 수정해서 확장한다.
3. 리스코프 치환 원칙 (Liskov substitution principle, LSP)
- 구상 객체는 추상 객체가 제공하는 기능을 온전히 수행 할 수 있어야 한다.
4. 인터페이스 분리 원칙 (Interface segregation principle, ISP)
- 범용적인 인터페이스 1개 보다는 구체적인 인터페이스 여러개로 나누어서 사용하는 것이 좋다.
5. 의존 역전 원칙 (Dependency inversion principle, DIP)
- 구상 클래스에 의존하기 보다는 추상 클래스에 의존하는 것이 더 좋다.
다음과 같은 원칙을 가지고 있다. 그러나 이 원칙만을 가지고 처음부터 객체지향을 설계하기란 여간 쉬운일이 아니다.
그러다 사람들은 어느 날 이러한 객체지향 설계의 원칙을 지키면서 개발을 하다보니 어떠한 공통점들이 존재한다는 사실을 알게 되었다. 그리고 이러한 공통점을 모아서 정리한 것이 바로 디자인 패턴이다. 디자인 패턴은 총 23개의 패턴이 존재한다.
디자인 패턴을 공부할 때에는 https://refactoring.guru/ 해당 사이트에 잘 정리되어있다.
회고
오늘도 코드리뷰 과제를 열심히 진행했다. 특히 정규식을 작성하는 부분에서 상당히 많은 시간이 할애되기도 했고, 객체지향적인 사고를 하기 위해 하나의 객체가 너무 많은 책임을 가지고 있는 것은 아닌가를 생각하면서 개발을 하다보니, 시간이 오래걸리기도 하였다. 하지만 확실히 책임의 입장에서 개발을 진행하다 보니 코드를 보기 쉬워지는 것을 직접 눈으로 보고 알 수 있었다. 추가적으로 오늘 디자인 패턴 수업을 들었는데, 데브코스 내에서 디자인 패턴 관련 스터디가 열려서 디자인 패턴 스터디를 진행하게 되었다. 현재 코딩 테스트 스터디와 디자인 패턴 스터디 두 개를 하게 되었는데, 평소에 정말 하고 싶었던 스터디들을 둘 다 하게 되어서 너무 좋다! 앞으로도 열심히 해야겠다!
내일은 디자인 패턴에 대해서 조금 정리를 하고 코드리뷰 과제를 마무리 할 계획이다! 아니 마무리가 되었으면 좋겠다.. 해야 할 것들이 너무나도 많다. 그래도 보람찬 하루를 보내고 있는 것 같아 뿌듯한 하루이다.
'프로그래머스 데브코스' 카테고리의 다른 글
프로그래머스 데브코스 6일차 - 전략패턴 (3) | 2023.06.08 |
---|---|
프로그래머스 데브코스 5일차 (0) | 2023.06.08 |
프로그래머스 데브코스 4일차 - 인터페이스 (0) | 2023.06.07 |
프로그래머스 데브코스 2일차 - 프레임워크를 위한 JAVA (0) | 2023.06.02 |
프로그래머스 데브코스 1일차! (0) | 2023.06.02 |