객체 지향 프로그래밍적 사고에 대해서 썻던 글에 대해서 몇 가지 피드백이 있었습니다.
- 예시코드가 불완전하고 알맞지 않음
- 추상화에 대한 설명을 더 구체적으로 할 것
- 객체간 상호관계성에 대한 내용의 부재와 근거(코드)의 부족
- 다이어그램의 상호관계성의 표시 부재
→ 사실 객체지향의 패러다임을 이해하기 위해서 작성하기 위한 글이었는데, 피드백을 듣고 막상 코드를 작성하려니 생각보다 정리했던 내용을 적용시키는 게 부자연스러웠습니다. 그래서 그 과정을 좀 자세히 기록하려고했습니다.
→ 이 과정에서 객체지향프로그래밍을 하는데 핵심적인 부분인 상호관계성에 대한 인지, 의존성에 대한 개념이 부족했다는 피드백이 있었습니다
부족한 부분들을 조금 더 채워서 객체지향 프로그래밍에 대해서 포스팅해보겠습니다.
객체지향 프로그래밍적 사고란 실제세계를 모델링하는 것 이라고 말할 수 있다고 했습니다. 이를 위해서 사물(객체)를 속성과 범주로 묶고 이를 추상화한다고 했습니다.
추상화란?
미술에서도 추상화는 어떤 물체를 사실적으로 표헌하는 것이 아니라 점,선,면,색채 등의 단순한 표현을 이용해서 그리는 것을 말합니다.
프로그래밍에서 추상화는 복잡한 자료, 모듈 등을 핵심적 개념이나 기능을 간추려내어 복잡도를 낮추는 것을 말합니다.
이전 글에서 판다, 원숭이 / 바나나, 대나무 를 각각 동물 과 음식 으로 나누었던것 기억하시나요?
데이터의 추상화는 이처럼 공통점을 모으고 차이점을 버릴 수 있도록 하는 것입니다.
이렇게 추상화를 함으로써 객체를 설계하는데 용이해지고 객체들 간 상호 관계를 정의하는 것도 간단해집니다.
객체지향적 사고 예제
먼저, 이전 글에서 예를 들었던 식당에서 음식을 주문하는 상황에 대한 코드를 조금 더 자세하게 작성해보겠습니다.
- 식당에 가서 손님이 음식을 주문하는 상황
여기서 객체로 분리했던 것은
- Customer 객체
- Menu 객체
- MenuItem 객체
- Chef 객체
- Pasta 객체
였습니다.
이에 따른 도메인 모델을 다이어그램으로 표현하면 다음과 같아집니다.
객체 지향 프로그래밍에서 어떤행동을 할 것인지 (message)를 먼저 정의하고 이 메세지를 수신할 객체를 선택합니다. ( 객체가 사물이어도 자율적으로 행동하는 존재로 생각합니다. )
인터페이스 설정
메세지와 이를 수신할 객체만을 추린다면 다음과 같아집니다.
이러한 메세지가 객체의 인터페이스를 결정하게 됩니다.
메세지를 결정하고 수신할 객체를 선택했고, 객체는 메세지를 자신의 인터페이스로 받아들입니다. 각 객체에서 협력관계를 배제하고 메세지 자체만 추려내면 다음과 같은 객체의 인터페이스가 됩니다.
코드로 설명하기
객체지향을 위해서 객체, 상호작용에 대한 관계를 대략적으로 파악했다면 이를 코드로 옮겨야합니다
- 먼저 객체를 정의하고 수신한 메세지를 통해서 어떤 일을 할 것인지 정해줍니다. (인터페이스 정의) ( 본래 Swift 에서는 protocol을 통해서 인터페이스를 정의하지만, 저는 예시를 위해서 각 객체안에 동작을 구현하는 것이 직관적으로 이해하기 더 좋은 것 같아서 이렇게 구현했습니다. )
struct Customer {
public func order() { }
}
struct Menu {
public func getMenu() { }
}
struct MenuItem { }
struct Chef {
public func cook() { }
}
struct Pasta { }
// 파스타 종류이름을 위한 enum
enum PastaName: String {
case alio = "알리오올리오"
case lagu = "라구파스타"
case carbo = "까르보나라"
}
- 각 객체가 메세지의 요청을 수행하도록 구현해 보겠습니다.
첫번째로
, Customer가 Menu와 Chef에 접근하기 위해서는 ( 메세지를 전송하기 위해서 ) 어떤 방식으로든 Menu와 Chef 객체를 알고 있어야합니다. 이번 글에서는 각 객체들을 order() 메서드의 파라미터로 이를 전달하겠습니다.
이를 위해서 Customer의 인터페이스를 수정해줍니다.
struct Customer {
public func order(menuName: PastaName, menu: Menu, chef: Chef) {
let selectMenuItem: MenuItem = menu.getMenuItem(selectName: menuName)!
let dish: Pasta = chef.cook(selectedMenu: selectMenuItem!)
...
}
}
두번째로
, Menu는 menuName에 해당하는 MenuItem을 찾아야 합니다. 이를 위해 Menu가 MenuItem을 포함하도록 합니다. 이때, 인터페이스에서는 객체의 내부 속성에 대한 어떤 힌트도 알아선 안됩니다.
struct Menu {
private var items: Array<MenuItem>
// 생성자
init(items: Array<MenuItem> {
self.items = items
}
// MenuItem의 목록을 검사하며 selectName과 같은 MenuItem을 찾습니다.
public func getMenuItem(selectName: PastaName) -> MenuItem? {
for i in self.items {
if i.name == selectName {
return i
}
}
return nil
}
}
struct MenuItem { }
세번째로
, Pasta는 스스로를 생성하기 위한 constructor(생성자)를 가집니다.
이름, 가격을 속성으로 가지고 생성자 안에서 MenuItem에 요청을 보내, PastaName과 같은 이름과 가격을 얻고 자신의 속성에 저장합니다.
MeniItem은 getName, getPrice에 대한 메서드를 구현합니다.
struct Pasta {
let name: String
let price: Double
init(menuItem: MenuItem) {
self.name = menuItem.getName()
self.price = menuItem.getPrice()
}
}
struct MenuItem {
let name: PastaName
let price: Double
init(name: PastaName, price: Double) {
self.name = name
self.price = price
}
public func getPrice() -> Double {
return price
}
public func getName() -> String {
return self.name.rawValue
}
}
네번째로
, Chef 에서는 meniItem을 받아서 cook() 하는 메서드를 구현합니다.
struct Chef {
public func cook(selectedMenu: MenuItem) -> Pasta {
let cookedPasta = Pasta(menuItem: selectedMenu)
return cookedPasta
}
}
여기까지 각 객체의 상호관계와 이에 따른 인터페이스정의, 속성정의를 통한 구현이 끝났습니다.
크게 봤을 때 중요한 점은 다음과 같습니다.
- 메세지를 정의하고 이를 수신하기에 적절한 객체를 선택한다.
- 책임에 따라 구현을 설계한다
- 메세지를 먼저 정하고 이를 처리할 책임이 있는 객체를 정한다.
- 인터페이스에서는 객체의 내부 속성에 대해서는 알아선 안된다(캡슐화; 이부분은 객체지향프로그래밍의 핵심개념을 따로 다루는 글을 통해서 자세하게 다루겠습니다!)
결론
실제 세계를 모델링한다 에서 객체에만 집중해서 글을 썼는데, 상호관계와 구현을 더 자세하게 작성해봤습니다!
캡슐화 ( 인터페이스는 객체의 내부속성을 알 수 없다 )나 추상화를 통한 상속등 객체지향의 특징까지 다 쓰기에는 글이 다소 길어질 것 같아 분리해서 작성하겠습니다.
피드백 주시면 성장에 도움이 됩니다!
'iOS' 카테고리의 다른 글
객체 지향 프로그래밍적 사고 - 3 : OOP의 4가지 특징 (1) | 2024.08.30 |
---|---|
동기(sync)/비동기(Async) & 블로킹(blocking)/논블로킹(Non-blocking) (0) | 2024.08.22 |
객체지향 프로그래밍적 사고 (0) | 2024.08.13 |
.DS_Store 파일이란? (0) | 2024.04.16 |
[iOS] Font Size, Frame에 Text 맞추기, adjustsFontSizeToFitWidth, minimumScaleFactor, sizeToFit (0) | 2023.10.21 |