객체지향 프로그래밍 (OOP)¶
1. 핵심 개념 4가지¶
캡슐화 (Encapsulation)¶
데이터와 메서드를 하나의 단위로 묶고, 접근 제어자로 정보를 은닉합니다.
class BankAccount {
private double balance; // 외부에서 직접 접근 불가
public void deposit(double amount) {
if (amount > 0) balance += amount;
}
public double getBalance() {
return balance;
}
}
- 장점: 유지보수 용이, 데이터 무결성 보호
상속 (Inheritance)¶
부모 클래스의 속성과 메서드를 자식 클래스가 물려받습니다. IS-A 관계입니다.
class Animal {
void eat() { System.out.println("먹는다"); }
}
class Dog extends Animal {
void bark() { System.out.println("멍멍"); }
}
Dog d = new Dog();
d.eat(); // 부모 메서드 사용 가능
d.bark();
- 장점: 코드 재사용, 계층적 구조 표현
다형성 (Polymorphism)¶
같은 메서드가 다른 객체에서 다르게 동작합니다.
오버로딩 (Overloading) — 컴파일 시간 결정 (정적 바인딩) - 같은 이름, 다른 매개변수
오버라이딩 (Overriding) — 실행 시간 결정 (동적 바인딩) - 부모 메서드를 자식이 재정의
class Animal {
void sound() { System.out.println("..."); }
}
class Dog extends Animal {
@Override
void sound() { System.out.println("멍멍"); }
}
class Cat extends Animal {
@Override
void sound() { System.out.println("야옹"); }
}
Animal a = new Dog();
a.sound(); // "멍멍" 출력 (동적 바인딩)
추상화 (Abstraction)¶
복잡한 내부를 숨기고 필요한 기능만 노출합니다.
abstract class Shape {
abstract double getArea(); // 구현 강제
}
class Circle extends Shape {
double r;
@Override
double getArea() { return 3.14 * r * r; }
}
2. 추상 클래스 vs 인터페이스¶
| 구분 | 추상 클래스 | 인터페이스 |
|---|---|---|
| 목적 | IS-A 관계 | CAN-DO 관계 |
| 상속 | 단일 상속만 | 다중 구현 가능 |
| 메서드 | 일부 구현 가능 | 기본적으로 추상 (Java 8+ default 허용) |
| 생성자 | 가능 | 불가 |
| 접근 제어자 | 모두 가능 | public만 |
| 변수 | 모든 타입 | static final (상수) |
// 추상 클래스 - IS-A 관계
abstract class Vehicle {
abstract void drive();
void refuel() { System.out.println("주유"); } // 구현 포함 가능
}
// 인터페이스 - CAN-DO 관계
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
// 다중 구현
class FlyingFish extends Animal implements Flyable, Swimmable {
public void fly() { }
public void swim() { }
}
3. SOLID 원칙¶
S — 단일 책임 원칙 (SRP)¶
클래스는 하나의 책임만 가져야 합니다.
// Bad: 하나의 클래스가 급여 계산 + 보고서 생성을 모두 담당
class Employee {
void calculatePay() { }
void generateReport() { }
}
// Good: 분리
class PayCalculator { void calculatePay() { } }
class ReportGenerator { void generateReport() { } }
O — 개방-폐쇄 원칙 (OCP)¶
확장에는 열려있고, 수정에는 닫혀있어야 합니다.
// Bad: 새 도형 추가 시 기존 코드 수정 필요
class AreaCalculator {
double calculate(Object shape) {
if (shape instanceof Circle) { ... }
else if (shape instanceof Square) { ... } // 수정 필요
}
}
// Good: 새 도형은 Shape를 구현만 하면 됨
interface Shape { double getArea(); }
class Circle implements Shape { ... }
class Square implements Shape { ... }
L — 리스코프 치환 원칙 (LSP)¶
자식 클래스는 부모 클래스를 대체할 수 있어야 합니다.
I — 인터페이스 분리 원칙 (ISP)¶
클라이언트가 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리합니다.
// Bad: 모든 기능이 하나의 인터페이스
interface Worker { void work(); void eat(); }
// Good: 역할에 따라 분리
interface Workable { void work(); }
interface Eatable { void eat(); }
D — 의존성 역전 원칙 (DIP)¶
구체적인 클래스가 아닌 추상화(인터페이스)에 의존합니다.
// Bad: 구체적인 클래스에 의존
class OrderService {
MySQLDatabase db = new MySQLDatabase();
}
// Good: 추상화에 의존 (의존성 주입)
class OrderService {
private Database db; // 인터페이스
OrderService(Database db) { this.db = db; }
}
4. 클래스 관계¶
| 관계 | 키워드 | 설명 | 예시 |
|---|---|---|---|
| IS-A | 상속 | 자식이 부모의 일종 | Dog IS-A Animal |
| HAS-A (합성) | 포함 | 전체가 부분을 소유 (강함) | Car HAS-A Engine |
| HAS-A (집합) | 포함 | 전체가 부분을 참조 (약함) | Team HAS-A Player |
| USES-A | 의존 | 일시적으로 사용 | Driver USES-A Car |
시험 포인트¶
- 오버로딩 vs 오버라이딩: 매개변수 다름 vs 메서드 재정의
- 정적 바인딩 vs 동적 바인딩: 오버로딩 vs 오버라이딩
- 추상 클래스 vs 인터페이스: 단일상속/일부구현 vs 다중구현/전체추상
- SOLID 원칙 5가지 각각의 의미와 예시
- 합성 vs 집합: 부분이 전체에 종속 vs 독립적으로 존재 가능