본문 바로가기
공부 자료/Java

[Java] 객체 지향 프로그래밍의 3요소

by 미노킴 2022. 11. 27.

*이 글의 내용은 제가 이해한 것을 바탕으로 작성되었습니다. 글의 내용 중 잘못된 내용이 있다면 댓글로 피드백 해주시면 감사하겠습니다.

 

이 글은 객체 지향 프로그래밍의 구성 요소 중 흔히 3요소라고 불리는 캡슐화, 다형성, 상속 과 추가적으로 추상화에 대해 이해한 내용들을 정리한 글이다. 안겔 레오나르드의 '자바 코딩 인터뷰 완벽 가이드' 라는 책의 내용을 주로 참고하였다.

0. 추상화

추상화는 사용자와 관련 있는 내용만 노출하고 나머지 세부 내용은 숨기는 개념이다. 추상화를 통해 사용자는 애플리케이션이 일을 수행하는 방법이 아니라 애플리케이션이 수행하는 일 자체에 집중할 수 있다. Java에서는 인터페이스와 추상 클래스를 통해 추상화를 구현할 수 있다. 인터페이스는 메서드의 사용 방법 (이름) 만 제공할 뿐 메서드의 내부 구조는 알려주지 않는다. 이것이 추상화다.

 

추상화는 내용을 노출하는 복잡성을 줄이고, 재사용성을 높이며 코드 중복을 방지하고 낮은 결합도높은 응집도를 유지하게 해준다. 또한 중요한 내용만 공개하여 애플리케이션의 보안과 재량권을 유지해준다.

 

현실의 예를 들어 설명해보자.

 

우리는 운전할 때 핸들이 무슨 일을 하고 페달이 무슨 일을 하는지 알고, 그 둘을 사용할 줄도 안다. 하지만 대부분의 사람들은 우리가 핸들을 돌리거나 페달을 밟을 때 내부에서 어떤 과정으로 기능하는 건지는 모른다. 그리고 그걸 몰라도 운전하는데에는 전혀 지장이 없다. 오히려 운전을 배울 때 복잡한 내부 과정을 몰라도 되기에 운전을 쉽게 배울 수 있는 것이다.

1. 캡슐화

캡슐화는 주로 코드와 데이터를 하나의 작업 단위인 클래스로 결합하고, 외부에서 클래스 내부 데이터에 직접 접근하지 못하도록 하는 방어막 역할을 한다. 캡슐화는 '정보 은닉 메커니즘'이라고도 불리며 각 객체가 클래스 내에서 객체의 상태를 비공개(private)으로 유지할 때 성립한다.

 

자바에서는 접근 제한자를 이용하여 캡슐화를 구현한다. private 제한자로 객체 상태를 외부로부터 숨기고 일부 public 메서드를 노출하여 외부에서 사용할 수 있게 할 때 캡슐화가 구현되었다고 볼 수 있다.

 

예를 들어 설명해보자.

 

Cat 클래스는 mood(기분), hungry(배고픔), energy(에너지)와 같은 필드로 구성할 수 있다. Cat 클래스의 외부 코드는 이러한 필드를 직접 수정할 수는 없지만 play, feed, sleep과 같이 클래스 상태를 내부적으로 수정하는 public 메서드를 호출할 수 있다. 또한, Cat 클래스는 meow와 같이 클래스 외부에서 접근할 수 없는 private 메서스도 가질 수 있다. 이것이 캡슐화이다.

 

2. 상속

상속은 다른 객체를 기반으로 새로운 객체를 만들 수 있게 하는 개념이다. 상속은 서로 다른 객체가 상당히 유사하고 몇 가지 공통된 로직을 공유하지만 완전히 동일하지는 않을 때 유용하다. 상속은 객체가 다른 객체의 코드를 재사용 할 수 있도록 허용하여 코드의 재사용성을 유지하고 상속 시 각 객체만의 로직도 추가할 수 있다. 따라서 상속을 구현할 때는 공통된 로직을 재사용하고 다른 클래스의 고유 로직을 추출해야 한다.

 

클래스의 상속 관계는 IS-A 관계라고 하며 부모-자녀 관계라고도 한다. IS-A 관계는 클래스의 계층 정의에 사용하는 작업 단위이다. 상속 관계는 Foo가 Buzz를 상속할 때 'Foo IS-A buzz'와 같이 표현할 수 있다. 예를 들어 '고양이 IS-A 고양이과의 동물' 또는 '기차 IS-A 교통 수단' 과 같이 상속 관계를 표현할 수 있다.

 

자바에서 상속은 '자식클래스 extends 부모클래스' 와 같은 형식으로 구현할 수 있다. 자식 클래스는 부모의 필드와 메서드를 재사용할 수 있으며 자신만의 필드와 메서드를 추가할 수 있다. 상속된 객체는 슈퍼 클래스 또는 부모 클래스라고 하며 상속 받은 객체는 서브 클래스 또는 자식 클래스라고 한다. 자바에서는 한 객체가 동시에 여러 개의 객체를 상속 받을 수 없다.

3. 다형성

다형성은 객체가 때에 따라 다르게 동작할 수 있게 해주거나 어떤 동작이 다른 방법으로 동작할 수 있도록 하는 성질이다. Java에서 전자는 메서드 오버라이딩으로, 후자는 메서드 오버로딩으로 구현할 수 있다.

 

메서드 오버로딩은 여러 개의 메서드가 동일한 이름을 지니고 있지만 매개변수가 다른 경우 컴파일러가 오버로드된 메서드 가운데 어떤 형식을 호출할 것인지 '컴파일 시간'에 식별할 수 있으므로 컴파일 타입 다형성이라고도 한다. 이때 오버로드된 메서드의 형태에 따라 객체는 다르게 동작한다. 예를 들어 메서드 오버로딩을 활용하면 Cat 클래스의 play 메서드를 매개변수 없이 play()로 사용할 수도 있고 play(시간), play(장난감, 시간) 등으로 사용할 수도 있다.

 

메서드 오버라이딩은 IS-A 관계가 있을 때 일반적으로 사용하는 방법이며 런타임 다형성 또는 동적 메서드 디스패치라고 한다. 보통 여러 가지 메서드를 포함하는 인터페이스 구현으로 시작하며 각 클래스는 특화된 동작을 수행하기 위해 인터페이스에 있는 메서드를 오버라이드 한다. Animal 이라는 인터페이스의 play() 라는 메소드를 상속 받은 Dog, Cat, Fox가 있다고 생각해보자. Animal 타입의 변수가 .play()라는 메소드를 사용하면 해당 변수가 참조하는 객체가 Dog인지 Cat인지 Fox인지에 따라 play() 메소드가 다르게 동작한다.

 

왜 저게 가능한걸까? Java의 다형성이 타입에 대한 혼란 없이 이 클래스들을 부모 인터페이스와 똑같이 사용할 수 있게 해주기 때문이다. Animal cat = new Cat() 에서 우변은 메모리에 Cat 객체를 생성하고 cat 변수는 Cat 객체의 주소를 참조하지만, cat 변수의 타입은 Animal 클래스이다. Java에서는 자식 타입이 부모 타입으로 자동 변환이 일어나기에 가능한 일이다. 런타임에 Java가 자동 변환이 일어나는 클래스들을 구분할 수 있고 어떤 타입인지 알고 있기 때문에 이를 런타임 다형성이라고 한다.

 

-참고 서적: '자바 코딩 인터뷰 완벽 가이드', 안겔 레오나르드

                   '혼자서 공부하는 자바', 신용권