객체지향 프로그래밍(OOP)은 객체를 중심으로 소프트웨어를 설계하는 프로그래밍 패러다임입니다. OOP의 주요 개념은 다음과 같습니다:
- 캡슐화 (Encapsulation):
- 객체의 데이터를 외부로부터 보호하고, 데이터에 접근할 수 있는 인터페이스를 제공.
- 클래스 내부에서 데이터를 숨기고, 필요한 경우에만 메서드를 통해 데이터에 접근하게 함.
- 정보 은닉 (Information Hiding):
- 캡슐화의 하위 개념으로, 클래스의 내부 구현을 외부에서 볼 수 없도록 숨김.
- 접근 제한자(private, protected, public)를 통해 제어.
- 상속 (Inheritance):
- 기존 클래스를 재사용하여 새로운 클래스를 생성.
- 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아 사용.
- 다형성 (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)의 장단점
장점
- 코드 재사용:
- 기존 클래스의 코드를 재활용하여 중복 코드를 줄임.
- 유지보수 및 확장성 향상.
- 일관성 유지:
- 부모 클래스의 수정 사항이 자식 클래스에도 반영되어 일관성을 유지.
- 다형성(Polymorphism) 지원:
- 상속을 통해 인터페이스 기반 설계와 동적 바인딩(dynamic binding)이 가능.
단점
- 강한 결합:
- 부모 클래스와 자식 클래스 간의 의존도가 높아짐.
- 부모 클래스가 변경되면 자식 클래스도 영향을 받음.
- 불필요한 상속:
- 잘못 설계된 상속 구조는 코드의 복잡성을 증가시킴.
- 모든 부모 클래스의 기능이 자식 클래스에 필요하지 않을 수 있음.
- 캡슐화 위반 가능성:
- 자식 클래스가 부모 클래스의 내부 구현에 접근하면 캡슐화 원칙이 약화될 수 있음.
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의 강력한 도구로, 다양한 객체를 하나의 인터페이스로 처리할 수 있는 유연성을 제공.
'정보' 카테고리의 다른 글
왜 UI가 Main Thread에서만 그려져야 하나? (1) | 2024.12.13 |
---|---|
프레임워크와 라이브러리 차이 (0) | 2024.11.28 |
Swift의 동시성(Concurrency) 프로그래밍에 대해 설명해주세요. (1) | 2024.11.23 |
GCD(Grand Central Dispatch)의 주요 개념과 사용 방법을 설명해주세요. (1) | 2024.11.20 |
동시성 프로그래밍에서 동기(Synchronous)와 비동기(Asynchronous)의 차이점은 무엇인가요? (0) | 2024.11.19 |