본문 바로가기

Swift 문서 탐방

[Swift 문서 탐방] OOP, POP에 관하여

 OOP란 무엇일까?

객체 지향 프로그래밍(Object-Oriented Programming, OOP)은 현실 세계의 사물을 코드로 모델링하여 문제를 해결하는 프로그래밍 패러다임입니다. OOP의 핵심은 클래스와 객체를 활용하는데 있으며, 클래스는 데이터와 동작을 정의하는 청사진이고, 객체는 이 클래스에서 생성된 실체입니다. 예를 들어, "Car"라는 클래스를 만들고 , "myCar"라는 객체를 생성해 속성(Property)과 동작(Method)를 구현할 수 있습니다. 

OOP의 4대 핵심 원칙

OOP의 핵심 원칙으로 캡슐화, 상속, 추상화, 다형성이 있습니다.

  • 캡술화는 데이터와 메서드를  하나로 묶는 개념으로, 외부 접근을 제한하여 데이터를 보호합니다.
  • 상속은 기존 클래스의 특성을 새로운 클래스에 물려주는 개념으로, 코드 재사용성과 확장성을 높입니다.
  • 추상화는 복잡한 실제 사물을 단순화하여 모델링하는 과정으로, 불필요한 세부사항은 숨기고 중요한 기능만 노출하여 코드의 명확성을 높힙니다.
  • 다형성은 동일한 인터페이스나 메서드 호출이 다양한 방식으로 동작할 수 있게 하는 특성으로, 코드의 유연성을 제공합니다.

OOP 설계 원칙 SOLID

소프트웨어공학에서 SOLID 원칙은 객체 지향 설계를 위한 다섯가지 기본적인 원칙을 말합니다. 이 원칙들은 소프트웨어를 더 유연하고, 관리하기 쉽고, 확장 가능하게 만드는 데 목적이 있습니다.

 

SOLID 원칙은 개발 과정에서 문제를 예방하고, 코드의 재사용성을 높이며, 유지보수를 용이하게 하는 데 큰 도움을 줍니다.

  • 단일 책임의 원칙(SRP): 클래스가 하나의 기능만 가지고, 그 기능을 완전히 캡슐화해야 한다는 원칙입니다. 이는 클래스의 변경 사유가 단 하나여야 함을 의미합니다.
  • 개방-폐쇄 원칙(OCP): 소프트웨어 구성요소(클래스, 모듈, 함수 등)는 확장에는 개방적이어야 하고, 수정에는 폐쇄적이어야 한다는 원칙입니다. 즉, 기존 코드를 변경하지 않고도 시스템의 기능을 확장할 수 있어야 한다는 의미입니다.
  • 리스코프 치환 원칙(LSP): 하위 타입은 언제나 그것의 기반 타입으로 대체될 수 있어야 한다는 원칙입니다. 이는 하위 클래스가 상위 클래스의 행위를 정확히 모방해야 하며, 상위 클래스의 인스턴스를 하위 클래스의 인스턴스로 대체해도 시스템의 정확성을 해치지 않아야 함을 의미합니다.
  • 인터페이스 분리 원칙(ISP): 사용하지 않은 인터페이스는 클라이언트에게 강제해서는 안 된다는 원칙입니다. 클라이언트는 자신이 사용하는 메소드에만 의존해야 하며, 불필요한 의존성은 제거되어야 합니다.
  • 의존성 역전 원칙(DIP): 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다는 원칙입니다. 이는 구체적인 구현보다는 인터페이스나 추상 클래스에 의존하도록 함으로써, 모듈 간의 결합도를 낮추고 유연성을 높이는 데 목적이 있습니다.

OOP의 한계점

  1. OOP는 참조 타입인 객체를 기반으로 구성되기 때문에 필연적으로 암묵적인 데이터 공유가 발생하며, 이로 인해 예기치 않은 상태 변화와 Race Condition과 같은 동시성 문제가 발생할 가능성이 높습니다.
  2. 클래스는 상속이 단일 상속만 가능해 여러 추상화를 동시에 모델링하기 어렵고, 강한 결합(Tight Coupling)으로 인해 유연한 설계가 제한됩니다.
  3. OOP에서는 동적 다형성을 지원하지만, 타입 간의 관계를 명확히 표현하기 어려워 잘못된 타입 추론이나 강제 다운캐스팅 문제가 발생할 수 있습니다.
  4. 클래스 기반 설계는 사용하지 않는 기능이나 데이터까지 포함하는 경향이 있어 코드의 효율성과 가독성을 떨어뜨는 문제가 있습니다.

POP의 등장

참조 타입의 암묵적 데이터 공유 문제, 단일 상속 제한, 강한 결합, 동적 다형성 문제, 불필요한 기능 포함 문제 등의 OOP의 한계점을 극복하기 위해 Swift 언어에 프로토콜 지향 프로그래밍(Protocol-Oriented Programming, POP) 패러다임이 도입되었습니다. 이는 코드의 유연성과 재사용성을 높이는데 중점을 둡니다. 왜냐하면 프로토콜을 통해 인터페이스를 정의하고, 이를 채택하는 타입들이 구현을 제공함으로써 다형성을 실현할 수 있기 때문입니다.

POP 란?

프로토콜 지향 프로그래밍(Protocol-Oriented Programming, POP)은 프로토콜을 중심으로 설계하여 코드의 재사용성과 확장성을 극대화 하는 프로그래밍 패러다임입니다. 여기서 프로토콜은 특정 작업이나 기능을 실행하기 위한 메소드, 프로퍼티, 그리고 다른 요구 사항들의 청사진입니다. 클래스, 구조체, 열거형은 이 프로토콜을 채택하여 정의된 요구 사항을 실제로 구현합니다.

POP로 OOP의 한계점을 극복한 점들

값 타입과 프로토콜을 통한 참조 타입의 암묵적 데이터 공유 문제 해결

참조 타입에서 발생할 수 있는 암묵적인 데이터 공유 문제를 값 타입과 프로토콜을 활용하여 해결했습니다. 이를 통해 데이터 변경이 독립적으로 이루어지며, 참조 타입의 공유 상태로 인해 발생할 수 있는 동시성 문제(Race Condition)를 방지할 수 있습니다.

 

다중 프로토콜 채택을 통한 단일 상속 제한 극복

Swift에서는 단일 상속만 지원하지만, 다중 프로토콜 채택을 통해 하나의 타입이 여러 프로토콜을 구현할 수 있어 다양한 추상화를 모델링할 수 있습니다. 이를 통해 클래스 상속 없이도 유연한 설계를 가능하게 합니다.

 

느슨한 결합과 모듈화를 통한 강한 결합 문제 극복

프로토콜을 활용하면 타입 간 의존성을 줄여 강한 결합을 느슨한 결합으로 전환하고, 코드의 모듈화를 강화할 수 있습니다. 이를 통해 설계 변경에 유연하게 대응할 수 있으며, 시스템 확장과 유지보수가 용이해집니다.

 

정적 다형성과 Self 요구사항을 통한 동적 다형성 문제 해결

동적 다형성에서 발생할 수 있는 문제를 해결하기 위해 프로토콜의 Self 요구사항을 활용하면 타입 간의 관계를 명확히 정의할 수 있습니다. 이를 통해 강제 다운캐스팅 없이도 타입 안전성을 보장할 수 있으며, 정적 다형성을 통해 보다 안정적이고 효율적인 코드를 작성할 수 있습니다.

프로토콜 확장을 통한 불필요한 기능 포함 문제 해결

불필요한 기능이 포함된 설계 문제를 해결하기 위해 프로토콜 확장을 활용할 수 있습니다. 프로토콜 확장은 공통 기능에 대한 기본 구현을 제공하여 중복 코드를 줄이고, 각 타입이 필요에 따라 기능을 선택적으로 확장할 수 있도록 설계할 수 있습니다.