GoF 디자인패턴 정의
- 객체지향 프로그램은 객체로 구성된다.
- 객체는 데이터와 이 데이터를 활용하는 프로시저(메서드)를 묶는다.
러스트의 객체지향적 성격
- 구조체(Struct)와 열거형(Enum)은 데이터를 가진다.
- impl 블록은 해당 데이터 타입에 대한 메서드(행동)를 정의한다.
- 따라서, 러스트는 GoF의 정의에 부합하는 객체지향적 언어이다.
- 단, 러스트에서는 구조체를 “객체”라 부르지는 않지만, 동일한 기능을 수행한다.
러스트에서의 캡슐화
- pub 키워드로 공개 여부를 제어한다.
- 공개하지 않은 요소는 기본적으로 비공개(private)이다.
러스트에서의 상속
- 클래스 기반 상속을 지원하지 않음.
- 매크로를 사용하지 않고 부모 구조체의 필드/메서드를 상속할 방법이 없음.
- 상속의 목적에 따라 트레이트(trait), 조합(composition), 제네릭(generic) 등으로 대체 가능.
코드 재사용
- 트레이트의 기본 메서드 구현. 트레이트에 기본 메서드 구현을 제공할 수 있음.
- 이를 통해 여러 타입이 공통된 메서드 로직을 공유할 수 있음.
- 트레이트의 기본 구현을 구체 타입에서 오버라이드 할 수 있음.
- 이는 OOP에서의 “자식 클래스가 부모 메서드를 오버라이드”하는 것과 유사함.
러스트에서의 다형성
- 상속 기반 다형성 대신 트레이트 객체(trait object)를 사용.
- 상속은 불필요한 코드 공유를 초래하고 설계 유연성을 떨어뜨릴 수 있음.
- 잘못된 하위 클래스 메서드 호출로 인한 오류 가능성이 존재하기 때문.
트레이트 객체 (Trait Object)
- 특정 트레이트를 구현한 타입의 인스턴스와 해당 타입의 **트레이트 메서드 테이블(vtable)**을 함께 가리킴.
- &dyn Trait 또는 Box<dyn Trait> 형태로 생성 가능.
- 구체 타입을 몰라도 트레이트를 구현한 모든 타입을 다룰 수 있음.
- 컴파일러는 해당 타입이 반드시 트레이트를 구현했음을 보장. 즉, 컴파일 타임에 모든 타입을 알 필요가 없음.
- 데이터와 동작을 결합한다는 점에서 객체지향의 “객체”와 유사. 단, 트레이트 객체에는 새로운 데이터를 추가할 수 없음.
- 목적은 공통된 동작의 추상화에 있음.
트레이트 객체와 디스패치 (Dispatch)
- 트레이트 객체를 사용하면 러스트는 동적 디스패치를 수행하면서 런타임 성능과 맞바꾸는 대신 유연성을 줄 수 있음
- 정적 디스패치 (Static Dispatch): 호출할 메서드가 컴파일 시점에 결정됨. 인라인 최적화 가능, 성능 손실 없음.
- 동적 디스패치 (Dynamic Dispatch): 호출 메서드를 컴파일 시점에 알 수 없을때, 런타임에 트레이트 객체 내 포인터를 통해 어떤 메서드가 호출될지 알아냄. 이 경우 런타임 비용이 발생하고, 인라인 최적화 불가능.
트레이트 객체 예제
// src/lib.rs
pub trait Draw {
fn draw(&self);
}
pub struct Screen<T: Draw> {
pub components: Vec<T>, // 트레이트 객체
}
impl<T> Screen<T>
where
T: Draw
{
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
pub struct Button {
pub width: u32,
pub height: u32,
pub label: String,
}
impl Draw for Button {
fn draw(&self) {
// 실제로 버튼을 그리는 코드
}
}
// src/main.rs
use gui::Draw;
struct SelectBox {
width: u32,
height: u32,
options: Vec<String>,
}
impl Draw for SelectBox {
fn draw(&self) {
// 실제 선택 상자를 그리는 코드
}
}
use gui::{Button, Screen};
fn main() {
let screen = Screen {
components: vec![
Box::new(SelectBox {
width: 75,
height: 10,
options: vec![
String::from("Yes"),
String::from("Maybe"),
String::from("No"),
],
}),
Box::new(Button {
width: 50,
height: 10,
label: String::from("OK"),
}),
],
};
screen.run();
}
'Programming > Rust' 카테고리의 다른 글
| 안전하지 않은 러스트 (0) | 2025.10.12 |
|---|---|
| 러스트 패턴과 매칭 (0) | 2025.10.11 |
| 러스트 동시성 (0) | 2025.10.09 |
| 러스트 스마트 포인터 (0) | 2025.10.09 |
| 러스트 카고와 crates.io (0) | 2025.10.09 |