OOP와 POP

Jee.e (황지희)·2022년 3월 28일
0

OOP

Object Oriented Programming / 객체지향 프로그래밍

독립된 객체를 만들고, 서로 협력을 통해 다른 객체와 Message를 주고 받으며 소프트웨어를 구성하는 것


✅ Alan Kay가 말하는 OOP의 본질

-설명
Messaging다른 객체의 데이터나 프로시져가 필요할 때는 메세지로 요청을 하고, 메세지를 받는 객체는 스스로 처리 방법을 선택한다.
Hiding of state-process (캡슐화)관련있는 데이터와 프로시져를 찾아서 묶고, 다른 객체가 내부를 건드리지 못하게 한다. (private을 우선 적용하라는 이유)
Extreme late-binding메세지를 받는 객체는 그때 그때 달라질 수 있다.
프로시저 : 데이터(상수)를 가지고 조건문, 반복문 등을 통해 일정한 절차를 수행하는 부분

✅ 본질 3가지를 합치면 ?

  • 변경 가능한 공유 데이터가 최소로 줄어든다.
    • 캡슐화를 통해 다른 객체가 접근할 수 없게 하기 때문이다.
    • 다른 객체의 상태를 알아내거나 바꾸려면, 해당 객체가 정해놓은 형식으로 메세지를 보내야한다.
  • 구현 부분을 쉽게 바꿀 수 있다.
    • 메세지를 받는 부분만 일관되게 유지된다면, 실제로 그걸 처리하는 코드는 바뀌어도 실행에 문제가 없다.
    • 메세지를 보내는 것 ≠ 메서드 호출
    • 다른 객체에게 “돈을 줘” 라는 메세지를 받았을 때, 지갑에서 돈을 꺼내주던 이체를 해주던 응답 하는 방식은 객체가 스스로 결정
  • 메세지를 실제로 처리하는 객체를 쉽게 변경할 수 있다.
    • 동적 바인딩은 다용도 드라이버 같은 느낌이다.
      손잡이는 하나지만, 그때 그때 새로운 드라이버를 바꿔끼면서 ‘다용도’를 만들어 낸다.
    • 그때 그때 동적 바인딩을 해야 하니까, 객체의 생성과 사용을 분리해야한다.

그리고, 객체지향의 정말 중요한 개념인

다형성(polymorphism)

  • 클래스의 상속은 Subclassing 이라고도 하는데, 이능 기존에 구현되어있는 부모 클래스를 확장, 및 변형하는 것을 말한다.
  • 주로 Override / Overriding 을 의미
    • 부모 클래스에서 상속 받은 메서드를 그대로 사용하지 않고, 자식 클래스에 맞게 변경하여 사용
    • 위 이미지를 예시로, Person의 eat 메서드는 집밥을 / Student eat은 급식을 / University eat은 학식을 먹게 하는 것이다.

같이 학습하는 Eddy의 말을 빌려 한 줄로 말해보자면, 캡슐화 되어있는 서로 호환 가능한 세포들이, 서로 메세지를 주고 받으면서 더 큰 소프트웨어를 구성하는 방식 이라고 할 수 있겠다!
(참고 문서에 있는 Eddy 벨로그를 통해 더 정확히 이해할 수 있다.)




POP

Protocol Oriented Programming / 프로토콜지향 프로그래밍

OOP의 단점을 보안한, Protocl을 통한 상속의 한계점을 탈피한 프로그래밍


✅ POP를 사용하는 이유?

1. 보다 유연한 프로그래밍
필요한 부분만 프로토콜로 분리할 수 있고, 다중 프로토콜을 구현할 수도 있다.
이 규칙을 Class / Struct / Enum 에 적용할 수 있어 OOP보다 유연한 프로그래밍이 가능하다.

2. 값 타입의 상속
Swift의 기본 타입(String, Int, Float 등)은 대부분 구조체로 구현이 되어있다.
Protocol과 Extension 사용시, 위처럼 상속이 되지 않는 타입들에게 공통된 기능 을 줄 수 있다.

3. 수평적인 기능 확장
Class는 하나의 상속만 가능하고 수직적인 구조를 고려하여야 하지만 Protocol은 마치 블럭처럼 기능을 추가할 수 있다.

4. OOP의 단점 보완
OOP는 상속을 통해 타입을 확장한다. 그로 인해 슈퍼클래스를 그대로 상속받아, 불필요한 프로퍼티들을 갖게 된다.
상속 구조를 사용하기 위해, 값 타입으로 정의해도 되는 모델들을 참조타입으로 정의해야 하기도 한다.
하지만, POP는 슈퍼클래스와 서브클래스의 사이가 독립적이다. 값/참조 타입을 모두 사용할 수 있다.


// 아래와 같은 프로토콜 구현시
protocol Cafe {
    func coffee()
    func dessert()
} 

// 공통된 기능을 줄 수있다.
class CafeOne: Cafe {
    func coffee() {
        print("아메리카노와 라떼가 있습니다.")
    }
    func dessert() {
        print("케이크와 마카롱이 있습니다.")
    }
}

class CafeTwo: Cafe {    
    func coffee() {
        print("아메리카노와 라떼, 바닐라 라떼가 있습니다.")
    }
    func dessert() {
        print("휘낭시에와 르뱅쿠키가 있습니다.")
    }   
}




OOP와 POP의 차이

OOPPOP
객체 지향프로토콜 지향
Subclassing을 통해 수직 구조로 타입을 확장Protocol을 통해 수평 구조로 타입 확장
단일 상속프로토콜 구현으로 다중 상속처럼 사용 가능
Reference 타입만 구현 가능Extension을 통해 Reference/Value 타입 모두 구현 가능



✅ POP를 유용하게 쓸 수 있는 예시

  • 참조타입 뿐만 아니라, 값 타입에도 동일한 기능을 구현하고 싶을 때
  • 객체간 메세지를 전달하고 싶을 때
protocol Receiveable {
    func received(data: Any, from: Sendable)
}

extension Receiveable {
    func received(data: Any, from: Sendable) {
        print("\(self) received \(data) from \(from)")
    }
}

protocol Sendable {
    var from: Sendable { get }
    var to: Receiveable? { get }
    
    func send(data: Any)
}

extension Sendable {
    var from: Sendable { return self }
    
    func send(data: Any) {
        guard let reciver: Receiveable = self.to else { return }
        
        reciver.received(data: data, from: self.from)
    }
}

// 수신, 발신 모두 가능한 메일
class Mail: Receiveable, Sendable {
    var to: Receiveable?
}

let myMail = Mail()
let yourMail = Mail()

myMail.to = yourMail
myMail.send(data: "안녕!") // myMail 이 yourMail 에게 "안녕!" 이라는 메세지 전송



참고문서
1. 객체지향의 사실과 오해 (일명 토끼책)와 야곰 책
2. Eddy의 벨로그
3. 프로토콜 참고 문서

profile
교훈없는 경험은 없다고 생각하는 2년차 iOS 개발자입니다.

0개의 댓글