나의 기록, 현진록

[Swift] 객체 지향 프로그래밍 Object-Oriented Programming, OOP 본문

Programming/Swift

[Swift] 객체 지향 프로그래밍 Object-Oriented Programming, OOP

guswlsdk 2025. 2. 7. 17:02
반응형

프로그램을 절차적으로 실행되는 명령어의 집합이기 보다 여러 독립적인 부품(객체)들이 유기적인 집합체로 파악하고자 하는 패러다임이다.

 

객체지향적 설계

- 자동차를 만든다고 할 때 수많은 부품의 결합과 연결로 하나의 완전한 자동차가 만들어지는 것처럼 프로그램의 일부분에 해당하는 부품, 즉 객체를 먼저 만들고 여러 객체들을 조립해서 하나의 완성된 프로그램을 만드는 방법론라고 볼 수 있다.

 

- 컴퓨터 부품을 갈아끼울 떄 해당하는 부품만 쉽게 교체하고 나머지 부품들은 건들이지 않아도 되는 것처럼 객체 지향적 원리를 잘 적용해둔 프로그램은 각각의 객체들이 독립적인 역할을 가지기 때문에 코드의 변경을 최소화하고 유지보수 하는 데 유리하다.

 

- 객체 지향적 설계로 인해 프로그램을 보다 유연하고 변경이 용이하도록 만들 수 있다.

 

객체 지향 프로그래밍은 자동차에 비유했듯 우리가 보고 인지하는 실제 세계를 흉내내어 가장 기본적인 단위, 객체들을 만들고 객체간의 유기적인 상호작용을 규정하여 프로그램을 발전시키는 프로그래밍 방법론으로서 보다 인간친화적이고 직관적인 코드를 작성하는데 용이하다.

 

 

객체

- 객체란 객체 지향 프로그래밍의 가장 기본적인 단위이자 시작점이며 모든 실재하는 대상이다.

- 객체 지향 프로그래밍에서 객체는 속성(State)과 기능(Behavior)을 분류한 후에 변수(var)와 함수(func)로 정의할 수 있다.

 

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

1. 추상화

- 객체 지향 프로그래밍에서의 추상화는 객체의 공통적인 속성과 기능에 대해서 추출하여 정의하는 것을 말한다.

- 추상화를 통해 공통 특성만을 다루기에 복잡성을 감소시킬 수 있다.

- 추상화된 코드는 변경이 발생할 때 해당 추상화 계층만 수정하면 되므로 유지보수가 용이하다.

- 추상화된 protocol로 다양한 객체들을 생성하고 재사용할 수 있다.

 

 

- 서울 지하철 노선도는 서울의 지리를 추상화시켜서 보여주는 좋은 예시이다. 불필요한 세부사항들은 제거하고 가장 본질적이고 공통적인 부분만 추출했기 때문이다.

- 자동차와 오토바이는 모두 이동수단이며 전진과 후진이라는 공통점을 가진다.

 

protocol Animal {
    var name: String { get }
    func makeSound()
}

class Dog: Animal {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func makeSound() {
        print("\(name): 멍멍!")
    }
}

class Cat: Animal {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func makeSound() {
        print("\(name): 야옹!")
    }
}

let dog: Animal = Dog(name: "바둑이")
let cat: Animal = Cat(name: "나비")

dog.makeSound()  // 바둑이: 멍멍!
cat.makeSound()  // 나비: 야옹!

 

강아지와 고양이는 이름과 울음소리라는 공통적인 속성과 기능을 가지고 있다. Animal 프로토콜로 추출하여 추상화할 수 있다. 강아지와 고양이뿐 아니라 Animal 추상화한 특성들에 맞는 다른 동물도 추가하여 확장할 수 있다.

 

 

2. 캡슐화

- 객체의 속성과 메서드 등을 외부에서 직접적으로 접근하지 못하도록 보호하는 개념이다.

- 접근제어자를 사용하여 데이터를 은닉, 내부 데이터에 직접적인 접근을 못하도록 하여 데이터의 무결성을 보장한다.

- 외부에서는 객체의 공개된 메서드를 통해서 데이터를 접근할 수 있어 데이터를 안정적으로 관리할 수 있다.

 

class BankAccount {
    private var balance: Double
    
    init(initialBalance: Double) {
        self.balance = initialBalance
    }
    
    func deposit(amount: Double) {
        balance += amount
        print("₩\(amount) 입금 완료. 현재 잔액: ₩\(balance)")
    }
    
    func withdraw(amount: Double) {
        guard balance >= amount else {
            print("잔액 부족! 현재 잔액: ₩\(balance)")
            return
        }
        balance -= amount
        print("₩\(amount) 출금 완료. 현재 잔액: ₩\(balance)")
    }
    
    func getBalance() -> Double {
        return balance
    }
}

let myAccount = BankAccount(initialBalance: 10000)
myAccount.deposit(amount: 5000)  // ₩5000 입금 완료. 현재 잔액: ₩15000
myAccount.withdraw(amount: 2000) // ₩2000 출금 완료. 현재 잔액: ₩13000

// 직접 접근 불가 (에러 발생)
// myAccount.balance = 100000

 

balance 속성을 private으로 설정하여 외부에서 직접 변경할 수 없도록 보호한다.

deposit(), withDraw() 메서드를 통해서만 잔액을 조작할 수 있도록 한다.

 

3. 상속

 

- 이미 존재하는 클래스의 속성과 메서드를 물려받아 새로운 클래스를 정의한다. 부모-자식 관계로서 클래스를 확장하거나 변경할 수 있다.

- 부모 클래스의 코드를 재사용하여 코드 중복을 줄일 수 있다.

- 자식 클래스는 부모 클래스의 기능을 확장하거나 추가적인 기능을 정의할 수 있다.

- 클래스 간의 계층적 구조가 형성되어 코드의 구조를 보다 명확하게 이해할 수 있다.

 

class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func introduce() {
        print("안녕하세요, 저는 \(name)입니다.")
    }
}

class Student: Person {
    var studentID: String
    
    init(name: String, studentID: String) {
        self.studentID = studentID
        super.init(name: name) // 부모 클래스의 초기화 호출
    }
    
    override func introduce() {
        print("안녕하세요, 저는 학생 \(name)이고 학번은 \(studentID)입니다.")
    }
}

let person = Person(name: "김철수")
let student = Student(name: "이영희", studentID: "20241234")

person.introduce()  // 안녕하세요, 저는 김철수입니다.
student.introduce() // 안녕하세요, 저는 학생 이영희이고 학번은 20241234입니다.

 

Student는 어차피 Person이기 때문에 Student가 Person을 상속하여 Person을 재사용할 수 있다. Student 클래스는 필요한 속성을 추가하여 확장시킨다.

 

4. 다형성

다형성은 다양한 클래스들이 같은 메서드나 인터페이스를 가진 객체들이 다양한 방식으로 동작할 수 있는 것을 말한다. 프로토콜과 상속을 이용하여 다형성을 구현할 수 있다.

 

- 새로운 클래스를 추가하거나 기존 클래스를 수정하지 않고도 코드를 확장하거나 변경할 수 있어 확장에 유연하다.

- 변경 사항을 최소화하여 기능을 수정하거나 추가할 수 있어 유지보수에 용이하다.

 

class Vehicle {
    func move() {
        print("탈것이 이동합니다.")
    }
}

class Car: Vehicle {
    override func move() {
        print("자동차가 도로를 달립니다.")
    }
}

class Airplane: Vehicle {
    override func move() {
        print("비행기가 하늘을 납니다.")
    }
}

let vehicles: [Vehicle] = [Car(), Airplane()]

for vehicle in vehicles {
    vehicle.move()
}

 

- Vehicle 클래스는 기본 move() 메서드를 가지고 Car와 Airplane은 move()를 오버라이딩하여 다른 동작을 수행한다.

반응형

'Programming > Swift' 카테고리의 다른 글

[Swift] 강한 참조 사이클(클로저)  (0) 2025.02.07
[Swift] ARC(Automatic Reference Counting)  (1) 2025.02.06
[Swift] Struct와 Class의 특징  (0) 2025.02.05
[Swift] 15.2 필터  (0) 2021.09.14
[Swift] 15.1 맵  (0) 2021.09.14