개요
필자는 자바에서 객체 지향 프로그래밍을 했지만 정확한 용도와 의미를 모르고 사용했었다. 이번 포스팅을 통해 객체 지향이라는 말이 정확히 무엇인지 알아보고 간단한 예제를 통해서 실습까지 해보는 시간을 가져보도록 하겠다.
* 참고로 필자는 Java를 알고 있다고 가정하고 설명하기 때문에 조금 불친절할 수 있습니다.
객체 지향 프로그래밍(OOP)이란?
소프트웨어를 객체라는 독립적인 단위로 나누고, 이러한 객체들의 상호작용으로 소프트웨어를 구성하는 프로그래밍 패러다임이다.
OOP의 중요 장점은 코드 재사용성 향상, 유지보수 향상, 코드 구성의 개념화, 코드의 가독성 향상이며 추상화, 캡슐화, 상속, 다형성이라는 아주 중요한 개념들이 있다.
그럼 객체 지향 프로그래밍 패러다임은 왜 만들어졌을까? 소프트웨어의 규모가 점점 커지고 복잡해지면서 절차 지향 프로그래밍 기법은 품질이 낮아지고 함수는 데이터의 처리 방식을 구조화했을 뿐 데이터 자체는 구조화하지 못했다. 이런 고민들이 점점 쌓이다 보니까 객체 지향 프로그래밍 패러다임(OOP)을 만들었다.
그럼 상속 -> 다형성 -> 캡슐화 -> 추상화 순으로 각 개념들이 무엇을 하는 녀석인지 알아보자.
(참고로 이해하려면 Class에 대한 이해가 있어야 함!)
1. 상속
자식 클래스는 부모 클래스의 프로퍼티들을 상속 받을 수 있고, 추가적으로 다른 프로퍼티들을 가질 수 있다. 상속을 부모가 자식에게 재산을 물려준다라는 느낌으로 가져가도 좋다.
프로그래밍 관점에서 본다면 기존 클래스에 기능을 재사용할 수 있으면서도 새로운 클래스에 새로운 기능을 추가할 수 있게 해 준다.
부모 클래스를 상위 클래스라고 부르고 자식 클래스를 하위 클래스라고 부른다. 상속을 사용하는 이유는 코드의 중복을 막을 수 있기 때문에 코드가 더 간단해지고 유지보수가 수월하기에 사용한다.
상속과 관련된 키워드를 몇 가지 보자.
- open : 상속이나 오버라이드(overrid)를 허용한다. 프로퍼티나 함수에 open 키워드가 붙으면 재정의를 할 수 있다는 소리이다.(오버라이드와 관련된 내용은 2번 다형성을 읽어주길 바란다.)
- super : 자식 클래스 입장에서 부모 클래스를 가리킨다.
상속을 위해서는 open 키워드를 사용해줘야 한다. open 키워드는 "이 클래스는 상속이 가능한 클래스이다!"라는 것을 명시해 준다.
2. 다형성
다형성은 하나의 객체가 여러 형태를 가질 수 있다는 개념이다.
같은 변수나 프로퍼티 이름과 함수 이름이 같을 경우가 있다. 하지만 내용이 다를 수 있다. 즉, 상황에 따라 다른 의미로 해석된다.
다형성에는 2 가지를 알아둬야 할 것이 있다. overriding(오버라이딩)과 overloading(오버로딩)이다. 이 둘은 발음은 비슷한데 의미는 전혀 다르다. 장점으로는 함수 이름을 낭비하지 않고, 같은 이름의 속성을 유지함으로써 속성을 사용하기 위한 인터페이스를 유지한다.
- overriding(오버라이딩) : 부모 클래스를 상속받은 자식 클래스에서 부모 클래스에 함수를 재정의하는 것을 의미한다. 즉, 상속한 부모 클래스의 함수를 다른 내용으로 바꿀 수 있다는 것.
- overloading(오버로딩) : 같은 이름의 함수가 사용되지만 다른 용도로 사용되며 그 결과물도 다르게 구현할 수 있다는 개념이다.
PolymorphismAnimal이 2개의 클래스에 상속했고 똑같은 함수를 재정의하여 다른 출력값으로 만들었다. 이게 오버라이딩(overriding)이다.
3. 캡슐화
캡슐화는 변수나 함수, 프로퍼티들을 객체 안에 묶는 것을 의미한다. 즉, 객체의 데이터를 외부에서 접근하지 못하도록 막는다.
외부에서 데이터에 값이나 객체 자체를 변경할 수 없도록 숨기는 것을 의미한다. 이를 데이터 은닉화라고도 한다.
정보 은닉을 하면 필요 없는 정보는 외부에서 접근하지 못하도록 제한하는 것으로 주로 접근 제어자 중 private 키워드를 사용한다.
만약 정보 은닉이 안되면 객체의 상태 정보를 누구나 접근이 가능해 변경 가능하며 이는 잘못된 데이터가 들어갈 수도 있다는 의미이다.
캡슐화는 정보 은닉을 할 수 있는 방법 중 하나이며 캡슐화 자체가 은닉은 아니다.
Kotlin에서는 자바와 동일하게 4가지 접근 제어자가 있다.
- public : 모든 곳에서 접근이 가능하다.
- private : 선언된 클래스 내부에서만 사용이 가능하다.
- protected : 선언된 클래스 또는 하위 클래스에서만 접근이 가능하다.
- internal : 같은 모듈에서만 접근이 가능하기 때문에 상위 모듈은 하위 모듈에 접근이 불가능하다.
private으로 프로퍼티나 함수를 정의하면 객체 내부에서만 접근이 가능하고 외부에서는 접근이 불가능하다. public은 기본 값이므로 지움 처리된다.
age 프로퍼티와 sum 함수는 private으로 선언되어 있기 때문에 외부에서 접근이 불가능해 사용하지 못한다.
4. 추상화
추상화는 복잡한 형식을 숨기기 위해 사용되는 단순한 형식이다. 즉, 프로퍼티나 함수들을 정의만 하는 것으로 객체의 상세함은 감추는 것이다.
Kotlin에서 추상화를 표현하기 위한 2가지 방법이 있다. 인터페이스(interface)와 추상(abstract)이 있다. 이 둘은 같은 개념인데 서로 다른 의미를 지닌다. 같은 객체를 만들어야 할 경우 한번 작성된 코드를 활용하여 동일한 객체를 만들 수 있다.
- 인터페이스(interface) : 생성자를 가질 수 없으며 다중 상속이 가능하다. 또 변수는 정의할 수 없으며 프로퍼티와 함수는 정의되거나 안 되거나 모두 가능하다.
- 추상(abstract) : 생성자를 가질 수 있으며 다중 상속이 불가능하다. 프로퍼티와 함수는 선언만 가능하며 상세한 코드(구현)는 abstract 키워드를 제거하면 된다.
추상 클래스(abstract)
추상 클래스는 클래스와 별로 다른 게 없지만 프로퍼티나 함수에 abstract 키워드를 붙여 선언만 할 수 있다.
abstract 키워드를 사용하지 않으면 클래스에서 했던 방식과 동일하게 적용된다.
추상 클래스를 상속받으면 반드시 부모 클래스의 구현체(프로퍼티, 함수)를 재정의해줘야 한다.(하나의 약속?) 정의하지 않으면 컴파일러가 에러를 던져준다.
'나이: 18'이 2번 나오는 이유는 init 때문에 그렇다. init은 해당 클래스가 인스턴스화되면 컴파일러가 init 키워드를 찾는 데 있다면 제일 먼저 실행시켜 준다.
인터페이스(interface)
interface는 변수를 선언할 수 없기 때문에 늦은 초기화 즉, getter를 사용해야 한다. 함수는 선언, 정의 둘 다 된다.
생성자는 사용할 수 없으며 사용하게 될 경우 에러를 만날 것이다.
마찬가지로 interface도 재정의를 해줘야 하며 해당 인터페이스의 함수도 사용할 수 있다. 기본 접근 제어자가 public이기 때문이다. 만약 sum 함수를 private 키워드를 사용하여 접근을 막을 수 있다.
객체 지향 프로그래밍 패러다임은 참 신기하고 잘 짜인 것 같다. 뭔가 코드를 깔끔해 보이게 작성할 수 있을 것만 같다. 이제는 정확하게 알았으니까 잘 사용할 수 있겠다.ㅎㅎ
잘못된 부분이나 부족한 부분이 있을 수 있습니다. 댓글로 남겨주시면 감사하겠습니다. 😊
'Kotlin' 카테고리의 다른 글
[Kotlin] 초기화(initialize), get() / set(), 열거형 클래스(Enum Class) (0) | 2024.12.04 |
---|---|
[Kotlin] 클래스(Class), 데이터 클래스(Data Class) (4) | 2024.12.04 |
[Kotlin] 예외(Exception) 처리하기 (0) | 2024.12.03 |
[Kotlin] 함수(Function), 람다(lambda) (4) | 2024.12.02 |
[Kotlin] JVM, Kotlin 컴파일 과정 (2) | 2024.12.02 |