본문 바로가기

정보

객체지향 프로그래밍(OOP)의 주요 개념에 대해 설명해주세요.

객체지향 프로그래밍(OOP)은 객체를 중심으로 소프트웨어를 설계하는 프로그래밍 패러다임입니다. OOP의 주요 개념은 다음과 같습니다:

  1. 캡슐화 (Encapsulation):
    • 객체의 데이터를 외부로부터 보호하고, 데이터에 접근할 수 있는 인터페이스를 제공.
    • 클래스 내부에서 데이터를 숨기고, 필요한 경우에만 메서드를 통해 데이터에 접근하게 함.
  2. 정보 은닉 (Information Hiding):
    • 캡슐화의 하위 개념으로, 클래스의 내부 구현을 외부에서 볼 수 없도록 숨김.
    • 접근 제한자(private, protected, public)를 통해 제어.
  3. 상속 (Inheritance):
    • 기존 클래스를 재사용하여 새로운 클래스를 생성.
    • 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아 사용.
  4. 다형성 (Polymorphism):
    • 하나의 인터페이스를 통해 여러 형태의 객체를 다룰 수 있는 능력.
    • 동일한 메서드가 클래스에 따라 다른 동작을 하도록 구현.

 

캡슐화(Encapsulation)와 정보 은닉(Information Hiding)의 차이점

항목 캡슐화 정보 은닉
개념 데이터를 묶어서 하나의 단위(객체)로 만듦 객체 내부의 세부 정보를 외부에서 숨김
목적 데이터와 메서드를 하나의 클래스 안에 포함 객체의 무결성과 보안을 유지
구현 방식 클래스, 메서드, 객체를 활용 접근 제한자 (private, protected) 사용
클래스 내부에 메서드와 속성을 정의 private를 통해 내부 변수를 숨기고 getter, setter 사용

 

class BankAccount {
    private var balance: Double // 정보 은닉
    
    init(initialBalance: Double) {
        self.balance = initialBalance
    }
    
    func deposit(amount: Double) { // 캡슐화
        balance += amount
    }
    
    func withdraw(amount: Double) -> Bool { // 캡슐화
        guard amount <= balance else { return false }
        balance -= amount
        return true
    }
    
    func getBalance() -> Double { // Getter (정보 은닉)
        return balance
    }
}

let account = BankAccount(initialBalance: 100.0)
account.deposit(amount: 50.0) // Encapsulation
print(account.getBalance()) // 150.0
// account.balance = 200.0 // 에러: balance는 private

 

 

  • 캡슐화: deposit, withdraw 메서드로 계좌 잔액을 관리.
  • 정보 은닉: balance를 private로 선언하여 외부에서 직접 접근 불가능.

 

상속(Inheritance)의 장단점

장점

  1. 코드 재사용:
    • 기존 클래스의 코드를 재활용하여 중복 코드를 줄임.
    • 유지보수 및 확장성 향상.
  2. 일관성 유지:
    • 부모 클래스의 수정 사항이 자식 클래스에도 반영되어 일관성을 유지.
  3. 다형성(Polymorphism) 지원:
    • 상속을 통해 인터페이스 기반 설계와 동적 바인딩(dynamic binding)이 가능.

 

단점

  1. 강한 결합:
    • 부모 클래스와 자식 클래스 간의 의존도가 높아짐.
    • 부모 클래스가 변경되면 자식 클래스도 영향을 받음.
  2. 불필요한 상속:
    • 잘못 설계된 상속 구조는 코드의 복잡성을 증가시킴.
    • 모든 부모 클래스의 기능이 자식 클래스에 필요하지 않을 수 있음.
  3. 캡슐화 위반 가능성:
    • 자식 클래스가 부모 클래스의 내부 구현에 접근하면 캡슐화 원칙이 약화될 수 있음.
class Animal {
    func makeSound() {
        print("Some generic animal sound")
    }
}

class Dog: Animal {
    override func makeSound() {
        print("Bark")
    }
}

class Cat: Animal {
    override func makeSound() {
        print("Meow")
    }
}

let animals: [Animal] = [Dog(), Cat()]
for animal in animals {
    animal.makeSound() // 다형성 활용: 각 객체에 맞는 메서드 호출
}

 

다형성(Polymorphism)을 활용한 예시

다형성은 하나의 인터페이스로 다양한 객체를 처리할 수 있게 합니다.

이는 코드 확장성과 유지보수성을 높이는 데 유용합니다.

protocol Shape {
    func area() -> Double
}

class Circle: Shape {
    let radius: Double
    
    init(radius: Double) {
        self.radius = radius
    }
    
    func area() -> Double {
        return Double.pi * radius * radius
    }
}

class Rectangle: Shape {
    let width: Double
    let height: Double
    
    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }
    
    func area() -> Double {
        return width * height
    }
}

// 다형성 활용
let shapes: [Shape] = [
    Circle(radius: 5),
    Rectangle(width: 4, height: 6)
]

for shape in shapes {
    print("Area: \(shape.area())")
}
Area: 78.53981633974483
Area: 24.0

 

 

  • shapes 배열에 Circle과 Rectangle 객체를 혼합해 저장 가능.
  • area() 메서드는 객체의 타입에 따라 다르게 동작.
  • 코드가 확장 가능하며 유연함.

 

결론

  • 캡슐화와 정보 은닉은 데이터를 보호하고 객체를 안전하게 설계하는 데 중요.
  • 상속은 코드 재사용성을 높이지만, 잘못된 설계로 인해 강한 결합과 불필요한 복잡성을 초래할 수 있음.
  • 다형성은 OOP의 강력한 도구로, 다양한 객체를 하나의 인터페이스로 처리할 수 있는 유연성을 제공.