본문 바로가기
TIL (Today I Learned)

[CS] 객체지향 프로그래밍(OOP)이란?

by Bokoo14 2024. 2. 12.

[참고]

더보기

 

📝 객체(Object)?

객체(object)는 사전적인 정의로 실제 존재하는 것을 말한다. 객체지향 이론에서는 사물과 같은 유형적인 것뿐만 아니라, 개념이나 논리와 같은 무형적인 것들도 객체로 간주한다. 프로그래밍에서의 객체는 클래스에 정의된 내용대로 메모리에 생성된 것을 뜻한다.

일반적으로 객체는 다수의 속성과 기능을 가지고 있으며, 객체가 가지고 있는 속성과 그 기능을 객체의 멤버라고 한다.

 

객체는 프로그램에서 사용되는 데이터 또는 식별자에 의해 참조되는 공간을 의미하며 값을 저장 할 변수와 작업을 수행 할 메소드를 서로 연관된 것들끼리 묶어서 만든 것을 객체라고 할 수 있다. 

 

객체를 쉽게 이해하기 위해서 우리는 붕어빵과 붕어빵 틀을 자주 예시로 들고는 한다. 붕어빵 모양 틀을 클래스, 붕어빵 모양 틀에 의해 만들어진 붕어빵을 객체라고 한다.

또한, 레고에 빗대 표현 할 수 있는데, 객체가 레고의 조각이 될 것이고 레고의 조각을 조립해서 무언가를 만드는 방식이 객체지향 프로그래밍이라고 할 수 있다.

 

📝 객체지향 프로그래밍 (OOP, Object Oriented Programming)?

프로그래밍에서 필요한 데이터를 추상화 시켜 상태와 행위를 가진 객체로 만들고, 객체들간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법

이미지 출처 :  https://www.reddit.com/r/ProgrammerHumor/comments/418x95/theory_vs_reality/

 

객체는 또한 레고 조각과도 비슷하게 여러군데에서 재사용 할 수 있는데 이는 부품화 와 재사용성 이라는 객체 지향 프로그래밍의 특징을 보여주기도 한다.

 

📝 객체지향 프로그래밍 언어

C++, C#, Java, Python, JavaScript, Ruby, Swift

 

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

캡슐화, 상속, 추상화, 다형성

캡슐화

  • 데이터 구조와 데이터를 다루는 방법들을 결합 시켜 묶는 것 (변수와 함수를 하나로 묶는 것을 뜻함)
  • 낮은 결합도를 유지할 수 있도록 설계하는 것
  • 객체의 수행 목적에 따라 데이터 구조 및 처리 방법을 결합시켜 묶어서, 외부에 내부 기능 구현 내용을 감추고 이용 방법만 알려줌
  • 외부 객체는 내부 객체의 구조를 알지 못하고 객체가 노출해서 제공하는 필드와 메서드만 이용할 수 있음

 

이미지 출처 :  https://velog.io/@nayeon/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-OOPObject-Oriented-Programming

 

속성과 기능을 정의하는 변수와 메소드를 클래스라는 캡슐에 넣어서 분류하는 것으로 재활용이 원활하다는 장점이 있고 캡슐화를 통해서정보은닉을 할 수도 있다. (접근제어자의 활용)

 

상속

  • 클래스의 속성과 행위를 하위 클래스에 물려주거나 하위 클래스가 상위 클래스의 속성과 행위를 물려받는 것을 말한다
  • 새로운 클래스가 기존의 클래스의 데이터와 연산을 이용할 수 있게 하는 기능
장점 단점
- 재사용으로 인한 코드가 줄어듦
- 범용적 사용 가능
- 자료와 메서드의 자유로운 사용 및 추가 가능
- 상위 클래스의 변경이 어려워짐
- 불필요한 클래스가 증가할 수 있다
- 상속이 잘못될 수 있다

 

추상화

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

이미지 출처 :  https://velog.io/@nayeon/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-OOPObject-Oriented-Programming

 

아우디, 니싼, 볼보 등은 모두 '자동차에 해당한다' -> '자동차'라는 추상화 집합을 만들어두고 자동차가 가진 공통된 특성을 만들어 활용

현대와 같은 다른 자동차 브랜드가 추가될 수 있다, 이때 추상화로 '자동차'를 구현해두면, 다른 곳의 코드를 수정하지 않고 추가로 만들 부분만 새로 생성해주면 된다.

 

다형성

  • 상속과 연관있는 개념, 한 객체가 다른 여러 형태(객체)로 재구성되는 것
  • 하나의 변수명, 함수명이 상황에 따라 다른 의미로 해석 될 수 있는 것
  • 어떠한 요소에 여러 개념을 넣어 놓는 것

OOP는 하나의 클래스 내부에 같은 이름의 행위를 여러개 정의(오버로딩)하거나 상위 클래스의 행위를 하위 클래스에서 재정의(오버라이딩)하여 사용할 수 있음

 

⭐️ 오버라이딩 (Overriding)

  • 상위 클래스가 가지고 있는 메소드를 하위 클래스가 재정의해서 사용하는 것

⭐️ 오버로딩 (Overloading)

  • 같은 이름의 메서드가 인자의 개수나 자료형에 따라 다른 기능을 하는 것


📝 객체 지향 설계 원칙 (SOLID 원칙)

로버트 마틴이 명명한 객체 지향 프로그램 및 설계의 기본 원칙

 

1️⃣ SRP (Single Responsibility Principle): 단일 책임 원칙

클래스는 단 하나의 책임을 가져야 하며, 클래스를 변경하려는 이유는 단 하나여야 한다.
  • 변화에 대한 유연성 확보하기 위해서 이 원칙을 지켜야 함.
  • 낮은 결합도, 높은 응집도를 추구하기 위해서 이 원칙을 지켜야 함.

2️⃣ OCP (Open-Closed Principle): 개방-폐쇄 원칙

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

열려있다: 기능 추가나 변경을 할 수 있다.

닫혀 있다: 기능 추가를 할 때, 그 모듈을 쓰고 있는 코드는 수정하지 않아야 한다.

 

OCP원칙을 지키기 위해서는 어떻게 해야할까❓

- 최대한 if/switch문을 안쓰도록 하자.

분기문을 모두 없애는 것은 불가능하니까, enum 같이 타입을 분기하는 지점에 대해서 분기처리를 안하는 것을 고려해보자.

 

굳이 분기처리를 하지 않아도 되는 코드를 protocol로 바꾸는 리팩토링 기술을, '조건부를 다형성으로 바꾼다.'라고 말한다고 한다.

조건의 분기와 일치하는 하위 클래스를 만들어서, 객체 클래스에 따라 다형성을 통해 적절한 구현이 달라지게 하는 방식이라고 한다.

(enum 자료형은, 유연성을 원하지 않는 시나리오에서나,ADT와 같은 시나리오에서 적합하다)

 

- dictionary 자료형 사용하기

직접적으로 ocp 를 지키는 구조는 아니다.

어차피 default 절이 있는 switch문과 동일한 약점을 갖는다. 때문에 case 가 자주 변경될 것 같을때는 피해야하고, 분기문을 없애고 싶을 때만 제한적으로 사용해보자.

 

3️⃣ LSP (Liskov Substitution Principle): 리스코프 치환 원칙

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

  • B가 A의 자식타입이면, 부모 타입인 A객체는 자식타입인 B로 치환해도, 작동에 문제가 없어야한다.
  • 부모클래스 타입인 A를 사용하는 기존의 프로그램 코드가 자식 클래스 B로 대입시켰을 때도 문제없이 작동하게 만들기 위해서, 자식 클래스는 부모 클래스가 따르던 제약사항을 모두 따라야한다.
  • 즉, 자식이 부모의 동작을 방해해서는 안된다라는 의미이다.

 

4️⃣ ISP (Interface Segregation Principle): 인터페이스 분리 원칙

객체가 반드시 필요한 기능만을 가지도록 제한하는 원칙

불필요한 기능의 상속/구현을 최대한 방지함으로써 객체의 불필요한 책임을 제가한다. 

  • 어떤 클래스를 인터페이스를 구현할 때, 사용하지 않는 메서드를 가지고 있는 인터페이스에 의존하게 하지 않아야 한다.
  • 클래스가 사용하는 기능만 제공하도록 인터페이스를 분리해야한다.
  • 즉, 클라이언트는 자신이 사용하지 않는 메소드에 의존관계를 맺으면 안된다는 뜻이다.
  • ISP는 SRP와 비슷하지만, 인터페이스를 통한 다른 해결책을 제시한다는 점에서 조금 차이가 있다.
  • 큰 규모의 객체는 필요에 따라 인터페이스로 잘게 나누어 확장성을 향상시킨다.

 

5️⃣ DIP (Dependency Inversion Principle): 의존 역전 원칙

고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.

= 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 한다. 

= 자신보다 변하기 쉬운 것에 의존하지 마라

  • 의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것 보다는 변화하기 어려운것, 거의 변화가 없는 것에 의존하라는 원칙
  • 상위클래스일수록, 인터페이스일수록, 추상 클래스일수록 변하지 않을 가능성이 높기에 하위 클래스나 구체 클래스가 아닌 상위클래스, 인터페이스, 추상클래스를 통해 의존하라는 원칙
  • DIP 를 어겼을 때의 해결 방법은, OCP와 비슷하다. 구체적인 클래스가 아닌, 인터페이스에 의존함으로써 DIP를 해결할 수 있을 것이다.

현재는 겨울이기 때문에 스노우 타이어를 구매하여 자동차에 끼도록 설계한 상태임.

즉, 고수준 모듈인 자동차가 저수준 모듈인 스노우 타이어에 의존하는 상태이다.

 

하지만, 날씨가 따뜻해지면서 더 이상 스노우 타이어를 사용할 필요가 없어졌다. 그래서 일반 타이어로 교체하기로 결정

그런데, 단순히 스노우 타이어를 일반 타이어로 바꾼다고 코드가 끝나는 것이 아니라, 이것에 의존하고 있던 자동차의 코드도 연쇄적으로 영향을 끼치게 된다. 

 

이것은 개방-폐쇄 원칙을 위반하는 것이므로 추상화나 다형성을 통해 문제를 고쳐야 한다. 의존 역전 원칙은 그 중에서도 추상화를 이용

스노우 타이어나 일반 타이어를 '타이어' 자체로 추상화하여 문제를 고친다. 

 

 

타이어는 저수준 모듈보다는 고수준 모듈인 자동차 입장에서 만들어지는데, 이것은 고수준 모듈이 저수준 모듈에 의존했던 상황이 역전되어 저수준 모듈이 고수준 모듈에 의존하게 된다는 것을 의미한다.

 

다만, 소스 코드의 의존은 자동차가 '타이어'를 의존하지만, 런타임에서의 객체 의존은 타이어가 아니라 하위 타이어 중 하나를 의존한다.

따라서, 의존 역전 원칙은 런타임에서의 의존을 역전시키는 것이 아니라 소스 코드 단계에서의 의존을 역전시킨다는 것을 유의해야 함.

 

'TIL (Today I Learned)' 카테고리의 다른 글

[CS] 동기(Sync), 비동기(Async)  (1) 2024.02.16
[Swift] ARC (Automatic Reference Counting)  (0) 2024.02.16
[Swift] Swift란?  (0) 2024.02.12
[CS] 메모리 구조  (1) 2024.02.10
[Swift] Class vs Struct  (0) 2024.02.10