📚개발지식/개념정리

[개념 정리] 객체 지향 프로그래밍(OOP)

뉴발자 2022. 10. 26.
728x90

 

 

 

 

 

개인적인 이해이므로 틀린 부분이나 자세한 부분은 댓글로 달아주시면 감사하겠습니다.

 

 

 

 

 

객체 지향 프로그래밍 (OOP)

 

 

 

 

객체 지향 프로그래밍

 

개발을 배우면서 제일 많이 들은 단어가 객체 지향 프로그래밍(OOP)였다.

 

회사 면접을 가도 기본적으로 물어보는 질문이었고 객체 지향 프로그래밍의 4가지 특징을 서술하라는 질문을 많이 받았다.

 

그래서 필자가 이해하고 검색해본 것을 바탕으로 객체 지향 언어의 개념을 정리해보려고 한다.

 

 

* 객체 지향 언어의 개념을 암기하는 것은 기억에 남지 않을 것 같아서 등장 배경부터 설명하고자 한다.

 

 


 

 

순차적 (비구조적) 프로그래밍의 등장

 

순차적 프로그래밍이란?

정의한 기능의 흐름에 따라 순차적으로 동작을 추가하며 프로그램을 완성하는 방식이다.

 

간단한 프로그램을 순차적으로 코드를 작성하면 코드가 매우 직관적이기 때문에 동작 방식을 쉽게 알아볼 수 있을 것이다.

 

하지만

 

프로그램의 규모가 조금이라도 커지면 코드를 읽기가 매우 힘들어지고

 

동작 중간에 코드를 추가할 일이 생기게 된다면 함수(goto)를 사용하거나

 

삽입될 부분을 찾아서 코드를 추가해주어야 하는데

 

이렇게 되면 순차적으로 동작이 된다는 장점이 사라지게 되고 코드가 꼬여서 스파게티 코드가 될 수 있다.

 

그래서 다음에 등장한 방식이 절차적(구조적) 프로그래밍이다.

 

 

 

 

절차적(구조적) 프로그래밍의 등장

 

절차적 프로그래밍이란?

절차적 프로그래밍은 단순히 순차적으로 명령을 수행하는 것이 아닌

 

반복되는 명령을 함수 및 프로시저 형태로 모듈화하여 사용하는 방식이다.

 

'Procedural'이란 단어의 번역이한국에서 '절차적'이라고 오역돼서 마치 순서(절차)에 따라 진행된다는 것이 중점적으로 보여지게 된다.

 

하지만

 

절차적 프로그래밍은 단순히 절차가 아닌 함수 호출을 통해서

 

코드의 추상화와 코드의 재사용성을 높아지는 장점이 있는 반면

 

프로시저라는 것 자체가 너무 추상적이라는 단점이 있다.

 

 

프로시저란?

  • 리턴 값이 없는 함수를 뜻한다.
  • 예를 들면 데이터를 단순히 출력하기 위한 printf 와 같은 함수를 프로시저라고 한다.

 

printf 도 리턴 값이 있긴하지만 본래 목적이 리턴 값과 무관하기 때문에 프로시저에 가깝다고 할 수 있다.

 

 

절차적 프로그래밍의 예시

인터넷 사이트의 회원 관리 프로그램을 구현한다고 생각해보면


  •  '회원'을 자료형 필드로 구현한다.
  •  '회원을 관리하기 위한 함수'를 구현한다.

하지만

 

절차적(구조적) 프로그래밍에서는 이 두개를 따로 생각할 수 밖에 없다.

 

회원은 회원이고 회원을 관리하는 함수는 따로 구현되기때문에

 

같은 코드안에 작성되더라도 둘의 연관 여부는 한번에 알아차리기 힘들 것이다.

 

즉, 논리적으로 묶여있을 수 없는 구조이기 때문에 동작이 추상적이라는 것이다.

 

따라서, 이를 보완하기 위한 패러다임으로 객체 지향 프로그래밍이 등장하게 된 것이다.

 

728x90

 

 

객체 지향 프로그래밍의 등장

 

어떤 개념에 대한 자료형과 함수들을 객체 형태로 함께 묶어서 관리하기위해서 객체 지향 프로그래밍이 등장하게 되었다.

 

중요한 점은 객체 내부에 자료형 필드와 함수가 함께 존재한다는 것이다.

 

가능한 모든 물리적, 논리적 요소들을 객체로 만드는 것객체 지향 프로그래밍이다.

 

위에 예시로 들은 회원 관리 프로그램에서 객체 지향으로 구현하게 된다면

 

회원의 정보(이름, 나이, 주소 등)와 같은 자료형 필드와

 

회원 가입, 회원 탈퇴 등의 메소드를회원이라는 객체에 한꺼번에 묶어서 관리하는 것이 가능해진다.

 

그러면 추상적이었던 동작들도 보다 직관적으로 보이게 되어 코드 가독성이 증가하는 효과를 볼 수 있다.

 

결과적으로 객체간의 독립성이 뚜렷하게 생기고 중복되는 코드의 양이 줄어들게 된다.

 

따라서 유지보수가 용이해진다.

 

하지만 코딩의 난이도가 상승하여 개발 속도가 느려지고 프로그램 실행 속도가 다른 개발 방식에 비해 느리는 단점도 존재한다. 

 

 

객체란?

말 그대로 대상을 나타내는 단어이다.

 

예를 들면 사람들 한명 한명을 객체라고 할 수 있고 책이나 동물 등 모든 것을 객체라고 할 수 있다.

 

사람은 생김새와 성격이 똑같을 수 없기 때문에 개개인을 객체라고 할 수 있으며,

 

책의 내용이 같더라도 책의 구겨짐이나 심지어 질감마저도 각 책마다 다르기 때문에

 

책 한권마다 하나의 객체라고 할 수 있다.

 

 

객체 지향 프로그래밍의 4가지 특징

객체 지향 프로그래밍이 생겨나면서 추상화, 캡슐화, 상속, 다형성 이 4가지 특징들을 갖추게 되었고, 

 

이 특징을 이해해서 더 나은 코드를 작성해야할 필요가 있다.

 

각 특징들의 기본적인 개념과 예시들을 보면서 확실하게 이해하고자 한다.

 

 

추상화 (Abstraction)

추상화란?

객체들의 공통적인 특성이나 메소드(동작) 하나로 묶는 작업이다.

 

 

추상화의 예시

추상화의 예시 - 도서관 정리 시스템

 

다들 한번쯤은 도서관에 가본적이 있을 것이다.

 

도서를 쉽게 찾기 위해서 이름순, 장르별로 진열되어있고 원하는 책이 있는 진열장에 가서 꺼내왔을 것이다.

 

즉, 사물의 공통적인 특징들을 파악한 후, 하나의 묶음으로 만들어 내는 것추상화인 것이다.

 

'스릴러'라는 하나의 추상화 집합을 만들어 두고,

 

'스릴러'라는 장르를 가진 책들의 공통적인 특징들 (반전, 공포, 액션 등) 을 만들어서 활용하면 된다.

 

또 새로운 스릴러 장르의 책이 입고됐을때 추상화로 구현되어진 코드를 활용하면

 

작성되어있는 다른 코드는 건드리지 않고 추가로 작성해야 하는 코드만 넣어주면 된다.

 

 

 

 

캡슐화  (Encapsulation)

캡슐화란?

은닉화를 통해 데이터와 코드의 형태를 외부로 부터 알 수 없게 하고 

 

높은 응집도와 낮은 결합도를 유지할 수 있도록 설계하는 작업이다.

 

어떤 부분에서 변화가 일어나도 다른 부분에 미치는 영향을 최소화 시키는 것을 의미한다.

 

즉, 객체 내부의 어떤 동작에 대한 구현이 어떻게 되어있는지 감추는 것이다.

 

이를 통해 외부에서 객체를 잘 못 건드려서객체를 손상시키는 것을 방지할 수 있다.

 

 

결합도란?

어떤 기능을 실행할 때 다른 클래스나 모듈에 얼마나 의존적인지를 나타내는 지표이다.

 

객체 지향 프로그래밍이 등장하게 된 이유 중 하나는 객체간의 독립성을 강조시키는 것이었다.

 

그런데 객체간의 결합도가 높다면 굳이 프로그램을 객체 지향으로 설계할 필요가 없어진다.

 

그러므로 독립적으로 생성한 객체들 간의 의존도를 최대한 낮게 구현하는 것이 중요하다.

 

객체 내의 모듈 간의 요소가 밀접하게 관련 있는 것으로 구성하여 응집도를 높이고,

 

객체 간의 결합도를 줄여야 유지 보수에 가장 좋은 설계이다.

 

 

* 높은 응집도와 낮은 결합도를 은닉화를 통해 이루어낼 수 있다.

 

 

은닉화란?

외부에서 접근할 필요 없는 요소들에 접근 제한자를 'private'로 두고 접근에 제한을 두는 것이다.

 

외부 객체는 객체 내부의 구조를 모르게 하고,

 

해당 객체가 노출됐을 때 제공하는 필드와 메소드만을 이용할 수 있도록 제한하여

 

예외적인 동작 오류를 방지하고 유지 보수 효율을 높일 수 있다.

 

 

캡슐화의 예시

캡슐화의 예시 - 도서관 대여 키오스크

 

도서 대여 시스템을 예시로 들어보겠다.

 

이용자가 도서를 대여할 경우 도서의 잔여 권수나 대여 가능 여부 등을 이용자에게 제공해준다.

 

하지만 도서를 대여하거나 반납한 사람의 이름이나 전화번호 등 불필요한 내부적인 정보는 제공하지않는다.

 

도서관의 내부적인 요소는 이용자들에게 제공하지 않고

 

대여에 필요한 도서 정보만을 제공해 주는 것이 캡슐화의 가장 중요한 특징인 은닉화이다.

 

 

 

 

상속 (Inheritance)

상속이란?

자식 클래스가 부모 클래스의 요소(필드, 메소드 등)를 그대로 물려받아 사용하거나 혹은 다듬어서 사용할 수 있게 해주는 것이다.

 

엄밀히 따지자면 상속은자식 클래스를 외부로부터 은닉하는 캡슐화의 일종이다.

 

 

상속의 예시

상속의 예시 - 자동차

 

자동차에 전혀 관심없는 A라는 사람이 있다고 치고 자동차라는 B클래스자식 클래스인 자동차의 종류를 담아둔 C클래스가 있다고 가정해보자.

 

A라는 사람 기준으로 자동차의 종류(C)는 숨겨져 있을 것이다.

 

자동차는 단순히 이동수단이라고 생각하는 A입장에서는 자동차의 종류가 어떤 것이든 크게 중요하지 않다.

 

그 어떤 차가 추가된다고 해도 중요한 것은 A의 입장에선 똑같은 자동차이고 A에게 영향을 주지 않는다는 것이다. 

 

따라서 캡슐화를 통해서 A의 입장에선 확인할 수 없도록 하는 것이다.

 

상속 관계에서는 단순히 하나의 클래스 안의 속성, 메소드들의 캡슐화에 한정되지 않고,

 

자식 클래스 또한 캡슐화되어 외부 클래스에 은닉하는 것으로 확장되는 것이다.

 

이렇듯 자식 클래스에 캡슐화해두면 외부에선 개별적인 자식 클래스들과 무관하게 개발을 이어갈 수 있다는 장점이 있다.

 

 

 

상속을 활용하면 상위 클래스의 요소들을 활용하면서 코드 재사용성이 높아진다.

 

하지만 상속을 활용한 코드 재사용은 객체 지향 프로그래밍에서 엄격하게 금지하는 행위중 하나이다.

 


 

상속 받은 부모 클래스의 변경이 불편해진다.

  • 부모 클래스에 상속한 자식 클래스가 많을 경우, 부모 클래스의 요소를 변경한다면 상속받은 자식 클래스에게도 영향을 미친다.

 

불필요한 클래스가 증가하게 된다.

  • 유사한 기능을 확장할 경우, 필요 이상의 불필요한 클래스를 만들어야할 수도 있다.

 

잘못된 상속을 사용할 경우

  • 같은 종류가 아닌 클래스의 구현을 재사용을 위해서 상속할 경우 문제가 발생할 수 있다.

 


 

이러한 이유들로 상속은 반드시 아래의 조건이 충족되는 경우에만 사용해야 한다.

 


상속 조건

  • IS-A의 관계가 성립되는 경우
  • 재사용의 관점 X, 기능 확장의 관점 O

 

무분별한 재사용화를 위한 상속은 객체 간의 독립성을 떨어트리고 결합도가 높아져 유지보수가 힘들어질 것이다.

 

 


 

IS-A 관계란?

  • 한 클래스가 다른 클래스의 자식 클래스임을 나타내는 포함 관계를 의미한다.
  • 예시 - 까마귀는 새다 (까마귀 클래스가 새 클래스에 포함된다.)

 

 

HAS-A관계란?

  • 한 객체가 다른 객체에 속하는 것처럼 상속이 아닌 구성의 관계를 의미한다.
  • 예시 - 나는 회사원이다 ('나'라는 객체는 단순히 회사의 구성원이다.)

 


 

 

 

 

다형성 (Polymorphism)

다형성이란?

하나의 요소로 여러 클래스에 수행 명령을 내렸을 때 각자의 설정된 방식으로 동작을 수행하는 것을 뜻한다.

 

다형성 구현을 통해 코드를 간결하게 해주고 유연함을 갖추게 해준다.

 

상속 관계에 있다면, 부모 클래스에 새로운 자식 클래스가 추가되어도

 

부모 클래스의 함수를 참조해오면 되기 때문에 다른 클래스의 영향을 받지 않는다.

 

 

다형성 예시

다형성의 예시 - 자동차

 

상속의 예시 중 자동차 클래스(B)자식 클래스(C)인 자동차 종류로 예를 들어보자.

 

사람마다 목소리가 다르듯 자동차마다 고유의 클랙슨 소리가 있고

 

같은 자동차여도 부품을 변경하면 클랙슨 소리를 변경할 수 있다.

 

자동차 클래스에 클랙션 소리 메소드를 작성하여 자식 클래스에서 동작시킬 경우

 

자식 클래스마다 각기 다른 클랙션 소리를 내는 것이 다형성이 나타난 부분이라고 할 수 있을 것이다.

 

다형성을 지원하는 방법은 오버라이딩(Overriding)오버로딩(Overloading)이 있다.

 

 

 


 

Overloading이란?

  • 하나의 클래스 안에서 같은 이름의 메소드를 여러 개 정의하는 방식
  • 단, 매개 변수의 개수나 타입이 달라야 한다.

 

 

Overloading 예시

1. 매개 변수의 개수가 다른 경우

public int overloadMethod() {
	return 0;
}
public int overloadMethod(String test) {
	return 1;
}

 

2. 매개 변수의 타입이 다른 경우

public int overloadMethod(int test) {
	return 0;
}
public int overloadMethod(String test) {
	return 1;
}

 

 

 

 

Overriding이란?

  • 부모 클래스로 부터 상속받은 메소드 내용을 변경하여 사용하는 방식
  • 단, 매개 변수와 리턴타입이 같아야한다.

 

 

Overriding 예시

public class Parent {
	public void overrideMethod() {
		System.out.println("부모 메소드 출력 내용");
	}
}
class Child extends Parent {
	public void overrideMethod() {
		System.out.println("상속받은 부모 메소드 내용을 수정해서 자식 메소드 내용으로 재사용");
	}
}

 

실행 결과

상속받은 부모 메소드 내용을 수정해서 자식 메소드 내용으로 재사용

 


 

 

 

 

참고 사이트

https://velog.io/@haero_kim/객체지향-프로그래밍-이해하기

 

객체지향 프로그래밍 제대로 이해하기

캡슐화를 핵심으로 두고 보는 OOP 의 핵심 포인트

velog.io

 

 

 

 

 

 

 

 

 

 

728x90

댓글