🧠 Why?

객체 지향 프로그래밍이 왜 필요할까?


  • 객체 지향 프로그램 이전의 패러다임을 살펴보면 먼저 순차적(비구조적) 프로그래밍절차적(구조적)프로그래밍이 있다.

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

  • 순차적으로 흘러가는 프로그래밍 구조를 의미
  • 순차를 중점으로 보는 코드
  • 코드의 흐름, 순서에 기반하는 프로그래밍

비구조적 프로그래밍에서는 주로 goto문을 활용
따라서 규모가 커질수록 goto문이 범람하게 되어 알아보기 어려운 코드가 만들어진다.

-> 코드의 중복을 피하기 위해 코드를 단위화할 방법을 모색



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

  • 절차적 프로그래밍에서의 절차란, 우리가 평소에 알고있는 절차가 아닌 프로시저를 의미한다.

프로시저란?
반환값(리턴)이 존재하지 않는 함수를 의미

  • 프로시저의 사용으로 goto문이 범람했던 순차적 프로그래밍과 달리 반복 가능성이 있는 부분을 프로시저로 쪼개고 각각의 프로시저안에서 중복되는 부분은 반복문으로 구성
  • 하지만 이런 프로시저는 추상적이라는 문제점을 가지고 있다.

예시

📚 도서관리 프로그램

도서관리 프로그램을 구현하기 위해선 책이라는 자료형과 이것을 사용할 함수가 필요할 것이다.
구조적 프로그래밍에선 책 자료형과 이것을 사용하는 함수가 물리적으로 같은 곳에 기록될 순 있지만(모듈) 논리적으로는(개념) 함께 할 수 없는 구조이다.
따라서 이를 묶기 위한 새로운 패러다임이 필요



⭐️ 이처럼 특정한 개념의 함수와 자료형을 한번에 묶기 위한 패러다임이 바로 객체지향 프로그래밍이다.



객체 지향 프로그래밍이란?

  • 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체로 만들고, 객체들간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법
  • 객체 내부에 자료형(필드)과 함수(메서드)가 함께 존재

📦 객체란?

값을 저장할 변수와 작업을 수행할 메소드를 서로 연관된 것들끼리 묶어서 만든 것
레고를 구성하는 레고 조각이라고 생각하면 이해하기 쉽다 !


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

1. 추상화 (Abstraction)

  • 객체에서 공통된 속성과 행위 추출
  • 공통의 속성과 행위를 찾아서 타입을 정의하는 과정
  • 불필요한 정보는 숨기고 중요한 정보만 표현 -> 프로그램을 간단하게 만듦

예시
아우디, BMW, 벤츠는 모두 ‘자동차’라는 공통점이 있다.
이 때, ‘자동차’라는 추상화 집합을 만들어 자동차들이 가진 공통적인 특징들을 만들어 활용



2. 캡슐화 (Encapsulation)

  • 변수와 함수를 하나로 묶는 것
  • 낮은 결합도를 유지할 수 있도록 설계하는 것

낮은 결합도란?

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

정보은닉의 활용

외부에서 접근할 필요가 없는 것들은 private으로 접근하지 못하도록 제한



3. 상속 (Inheritance)

  • 클래스의 속성과 행위를 하위 클래스에 물려주거나 하위 클래스가 상위 클래스의 속성과 행위를 물려받는 것
  • 새로운 클래스가 기존의 클래스의 데이터와 연산을 이용할 수 있게 하는 기능
  • 일반화 관계 (Generalization)라고도 하며, 여러 개체들이 지닌 공통된 특성을 부각시켜 하나의 개념이나 법칙으로 성립하는 과정

상속 재사용의 단점

  1. 상위 클래스 (부모 클래스)의 변경이 어려워진다.
  2. 불필요한 클래스가 증가할 수 있다.
  3. 상속이 잘못 사용될 수 있다.


4. 다형성 (Polymorphism)

  • 하나의 변수명, 함수명이 상황에 따라 다른 의미로 해석될 수 있는 것
  • 서로 다른 클래스의 객체가 같은 메세지를 받았을 때 각자의 방식으로 동작하는 능력

부모 클래스의 메소드를 자식 클래스가 오버라이딩해서 자신의 역할에 맞게 활용하는 것



객체 지향 설계 원칙 (SOLID)

1. 단일 책임 원칙 (SRP, Single Responsibility)

  • 하나의 클래스는 단 한 개의 책임을 가져야 한다.
  • 클래스를 변경하는 이유는 단 한개여야 한다.


2. 개방-폐쇄 원칙 (OCP, Open-Closed)

  • 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
  • 기능을 변경하거나 확장할 수 있으면서, 그 기능을 사용하는 코드는 수정하지 않는다.

3. 리스코프 치환 원칙 (LSP, Liskov Substitution)

  • 상위 타입의 객체를 하위 타입의 객체로 치환해도, 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.

4. 인터페이스 분리 원칙 (ISP, Interface Segregaiton)

  • 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.
  • 각 클라이언트가 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받지 않도록 만들어야 한다.


5. 의존 역전 원칙 (DIP, Dependa)

  • 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.
  • 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 한다.
  • 저수준 모듈이 변경되어도 고수준 모듈은 변경할 필요가 없다.