본문 바로가기

Programming Paradigm

2. 객체지향 프로그래밍 OOP

두 번째 패러다임 - 객체지향 프로그래밍 Object Oriented Programming

1. 객체지향이란


  • 정의 : 객체 등 의 집합으로 프로그램의 상호작용을 표현하며, 데이터를 객체로 취급하여 객체 내부에 선언된 메서드를 활용하는 방식.
  • 설계에 많은 시간이 소요되며, 처리속도가 다른 프로그래밍 패러다임에 비해 상대적으로 느림
  • 특징 4가지 : 추상화 / 캡슐화 / 상속성 / 다형성
  • OOP 설계 원칙 5가지 : 단일 책임 원칙 / 개발 폐쇄 원칙 / 리스코프 치환원칙 / 인터페이스 분리 원칙 / 의존관계 역전 원칙

2. 객체지향의 4가지 특징 A.E.I.P


(1) 추상화 Abstraction
- 복잡한 시스템으로부터 핵심적인 개념 또는 기능을 간추려내는 것.
- 클래스의 공통적인 특성(변수, 메서드)들을 묶어 표현하는 것.

(2) 캡슐화 Encapsulation
- 객체의 속성과 메서드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것.
- 데이터와 코드의 형태를 외부로부터 알 수 없게 은닉하고, 데이터의 구조와 역할, 기능을 하나의 캡슐 형태로 만드는 방법. (정보 은닉)
- 쉽게 설명하자면,만약에 호텔에 카드키가 있고 카드키로는 문만 열 수 있는 도어락이 있다면, 손님은 카드키로는 문을 열 수만 있고, 비밀번호를 바꾸거나 문이 열리는 원리를 변경할 수 없음.

(3) 상속성 Inheritance
- 상위 클래스의 특성을 하위 클래스가 이어받아 재사용하거나 추가, 확장하는 것.
- 코드의 재사용 측면, 계층적인 관계 생성, 유지 보수성 측면에서 중요.
- (부모 클래스에 정의된 변수 및 메서드를 자식 클래스에서 상속받아 사용함.)

(4) 다형성 Polymorphism
- 하나의 메서드나 클래스가 다양한 방법으로 동작하는 것.
- 대표적으로 오버로딩과 오버라이딩이 있음

  • 오버로딩
    동일 이름을 가진 메서드를 여럿 두는 것. 메서드의 타입, 매개변수 유형, 개수 등으로 여러 개 둘 수 있으며,
    컴파일 중에 발생하는 '정적'다형성임.
    하나의 클래스 안에서 동일 이름의 메서드를 여러개 정의하는 것.
  • 오버라이딩
    (일반적으로 메서드 오버라이딩을 지칭) 상위 클래스로부터 상속받은 메서드를 하위 클래스가 재정의하는 것.
    런타임 중에 발생하는 '동적'다형성임.
    부모 클래스로부터 상속받은 메서드 내용을 변경하여 사용하는 것으로 매개변수와 리턴 타입이 동일해야 함.
    쉽게 설명하면, 크레이지 아케이드에는 다양한 캐릭터가 있다. 크레이지 아케이드 캐릭터를 부모 클래스로 정의하고,
    배찌, 우니, 마리드를 하위 클래스로 생성해보자.
    부모 클래스에는 캐릭터가 물을 쏘는 스킬 메서드가 존재하고, 뛰기 스킬 메서드가 존재한다.
    캐릭터마다 물 쏘기 강도와 뛰기의 스피드가 다를 것이다.
    따라서 부모 클래스인 크레이지 아케이드 캐릭터에서 상속받은 메서드(물 쏘기, 뛰기)를 각각의 하위 클래스(배찌, 우니, 마리드)에서
    오버라이딩해서 서로 다른 물 쏘기 강도, 뛰기 속도를 갖도록 해야 한다.
    오버라이딩을 안 하면, 부모 클래스로부터 상속받은 메서드를 하위 클래스가 재정의(오버라이딩)하는 것이 아니라,
    배찌, 우니, 마리드 각각 하나씩 선언하고 그 각각의 캐릭터들이 쓸 모든 물 쏘기, 뛰기를 다 일일이 하드코딩 해야한다.
    따라서 코드 복잡성도 높아질 뿐만 아니라 코드 일관성이 떨어질 수도 있을 것이다.

3. 객체지향의 5가지 설계 원칙 SOLID Rule


Single responsibility principle / Open closed princilple / Liskov substitution principle / Interface segregation principle / Dependency inversion principle

(1) 단일 책임 원칙 SRP, Single Reposibility Principle
- 객체는 오직 하나의 책임만 가져야 함.
- 모든 클래스는 각각 단일 책임만 가져야 함
(예를 들어 A라는 로직이 있다면, 어떤 클래스는 A에 관한 클래스여야 하고 이를 수정한다고 해도 A에 관련된 수정 이어야 함. )
- 사칙연산 함수를 가지고 있는 계산 클래스가 있다고 가정하자. 이 상태의 계산 클래스는 오직 사칙연산 기능만 책임진다. 만일 프로그램 유지보수를 진행해도, 계산 클래스가 수정되어야 할 사유는 "사칙연산"함수와 관련된 문제가 있을 때만 계산 클래스를 수정해야 한다. 한국어 단어를 영어 단어로 바꿔주는 기능과 관련된 문제가 있어서 계산 클래스를 수정해서는 안된다는 의미다. 이처럼 단일 책임 원칙은 클래스 목적을 명확히 함으로써 구조가 난잡해지거나 불필요한 수정 사항의 범위가 넓어지는 것을 예방하고 기능을 명확히 분리할 수 있게 함.

(2) 개방-폐쇄 원칙 OCP, Open Closed Principle
- 객체는 확장에 대해서는 개방적이고 수정에 대해서는 폐쇄적이어야 한다는 원칙.
- 즉, 기존 코드는 잘 변경하지 않으면서도 확장은 쉽게 할 수 있어야 한다는 원칙
- 만약 객체 하나를 수정해야 할 때, 그 객체만 수정하는 것이 아닌 그 객체에 의존하는 다른 객체를 모두 고쳐야 한다면 좋은 설계로 보기 어려움. 실상황에 빗대자면, 일반적으로 라이브러리를 사용하는 객체의 코드가 변경된다고 해서 라이브러리 코드까지 변경하지는 않음.
- 개방-폐쇄 원칙은 각 객체의 모듈화와 정보 은닉의 올바른 구현 추구를 통해 객체 간 의존성 최소화하여 코드 변경에 따른 영향력 감소를 위한 원칙.

(3) 리스 코프 치환 원칙 Liskov Substitution Principle
- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함.
- 자식 클래스는 언제나 프로그램 정확성을 깨뜨리지 않으면서 자신의 부모 클래스를 대체할 수 있다는 원칙.
- 해당 원칙은 상속의 본질로서 이를 지키지 않으면 부모 클래스의 본래 의미가 변하기에 is-a 관계(EX 상속관계, '사람은 동물이다', '소도 동물이다')가 망가져 다형성이 안 지켜짐.

(4) 인터페이스 분리 원칙 ISP, Interface Segreation Principle
- 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 함.
- 하나의 general 한 인터페이스보다 구체화된 여러 개의 인터페이스를 만들어야 하는 원칙.
- 아래 이미지 <ISP 위반>에서 User1은 op1, User2는 op2, User3는 op3을 각각 사용하는 상황일 때, 클라이언트는 자신이 사용하지 않는 op1/2/3에도 의존하고 있음.
- 만약 op2의 소스코드가 변경 되면, User1을 컴머파일하여 배포해야 하는 문제가 있음.

(5) 의존성 역전 원칙 DIP, Dependency Inversion Principle
- 추상성이 높고 안정적인 고수준의 클래스는 구체적이고 불안정한 저수준 클래스에 의존해서는 안됨.
- 상위 계층은 하위 계층의 변화에 대한 구현으로부터 독립적이어야 함.

Reference

도서 : 면접을 위한 CS 전공지식 노트

https://jongminfire.dev/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80
https://namu.wiki/w/%EA%B0%9D%EC%B2%B4%20%EC%A7%80%ED%96%A5%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%EC%9B%90%EC%B9%99
https://blog.itcode.dev/posts/2021/08/14/open-closed-principle
https://steady-coding.tistory.com/385