📌 1. 추상화 (Abstraction)
- 추상화: 공통적인 특징을 추출하고, 불필요한 세부사항은 생략하여 핵심만 표현하는 방법.
- 예시: 리모컨의 버튼을 사용하면서 내부의 복잡한 구조는 알 필요 없음.
장점:
- 코드 재사용성 향상 → 유지보수성 증가
- 유연한 설계 가능
- 복잡성 감소로 이해와 설계가 쉬워짐
🔸 행위 중심 추상화 vs 데이터 중심 추상화
구분 |
행위 중심 추상화 |
데이터 중심 추상화 |
핵심 |
객체의 행위 및 메소드 |
객체의 데이터 및 속성 |
정의 방식 |
수행 가능한 행동으로 정의 |
포함된 데이터 구조로 정의 |
특징 |
메소드 기반 설계 |
데이터 구조 관리 중심 설계 |
- 일반적으로 객체 지향 프로그래밍(OOP)은 행위 중심의 추상화를 사용함.
(단, DTO 등 특정 경우에는 데이터 중심도 가능)
🔸 추상클래스와 인터페이스 비교
구분 |
추상클래스(Abstract Class) |
인터페이스(Interface) |
목적 |
공통된 속성, 메소드 정의 |
행동에 대한 규약 정의 |
메소드 |
추상 메소드 + 구현된 메소드 |
추상 메소드만 가능 (자바8 이후 default 메소드 제외) |
변수 |
멤버 변수 가능 |
상수만 가능 |
상속 |
단일 상속 |
다중 구현 가능 |
- 상속 계층 설계가 필요하면 추상 클래스
- 기능만 정의하고 실제 구현을 위임하고 싶으면 인터페이스
🔸 is-a, has-a, can-do 관계
관계 |
의미 |
예시 |
is-a |
A는 B이다 (상속) |
Sonata is-a Car |
has-a |
A는 B를 가지고 있다 (컴포지션) |
Person has-a Eye |
can-do |
A는 특정 행동 가능 (인터페이스) |
Bird can-do Fly |
* OOP에서 중요한 것은 객체와 객체간의 상호작용이다. 따라서 객체의 행위 중심으로 추상화 기법을 사용해 설계하는것이 중요하지만, 필요에 따라 데이터를 중심으로 추상화하여 설계하는 경우도 존재함 (Data Transfer Object DTO)
📌 2. 다형성 (Polymorphism)
- 하나의 객체가 여러 가지 타입을 가질 수 있는 특성
- 동일한 메시지로 서로 다른 객체가 각자의 방식대로 동작
필요성:
- 여러 객체를 하나의 타입으로 관리 → 유지보수, 생산성 향상
- 객체별로 다른 동작 가능 → 메소드 관리 수 감소, 유지보수 용이
- 높은 확장성 (새로운 클래스 추가가 쉬움)
- 결합도를 낮춤
Factory[] factories = new Factory[3];
factories[0] = new Samsung(); // Samsung은 Samsung 타입이면서 Factory 타입임
factories[1] = new LG(); // LG는 LG 타입이면서 Factory 타입임
factories[2] = new Apple(); // Apple은 Apple 타입이면서 Factory 타입임
// 반복문을 통해 각 공장을 가동
for (Factory factory : factories) {
factory.start();
}
class Samsung extends Fatory {
void start() {
System.out.println("삼성 공장 가동");
}
}
class LG extends Fatory {
void start() {
System.out.println("LG 공장 가동");
}
}
class Apple extends Fatory {
void start() {
System.out.println("Apple 공장 가동");
}
}
// 다형성을 미적용 : 객체별로 기능을 기억하고 사용해야 함
factories[0].start();
factories[1].start();
factories[2].start();
// 다형성 적용 : 관리해야 하는 메세지 수가 줄어듬
for (Factory factory : factories) {
factory.start();
}
🔸 오버라이딩(Overriding)
- 부모 클래스의 메소드를 자식 클래스에서 재정의하여 사용하는 기법
- @Override 어노테이션은 컴파일 단계에서 오버라이딩 여부를 검증해줌
- 협업 시 코드의 명확성 및 유지보수성 증가
🔸 동적 바인딩(Dynamic Binding)
- 컴파일 시 부모의 메소드와 연결되었다가, 실행 시 자식 클래스의 오버라이딩된 메소드로 바뀌어 실행되는 것
Factory samsung = new Samsung();
samsung.start(); // 컴파일 시 start()는 Factory의 start()를 가리키고 있음
// 이후 런타임 시 Samsung 인스턴스 객체의 오버라이딩된 메소드로 바인딩해 동작 하는것을
// 동적 바인딩(Dynamic Binding)이라고 함
📌 3. 캡슐화 (Encapsulation)
- 객체의 내부 데이터와 메소드를 숨기고 외부에 인터페이스만 노출하는 기법
- 데이터 보호, 결합도 감소, 유지보수 용이성을 목표로 함
필요성:
- 내부 구현 방식 변경 시 외부 영향을 최소화
- 데이터 무결성 유지
- 결합도 낮아짐 → 유지보수 및 확장 용이
📌 4. 상속과 컴포지션
🔸 상속(Inheritance)
- 부모 클래스의 필드, 메소드를 자식 클래스가 그대로 물려받아 사용
- 부모 클래스의 타입도 물려받아 다형성 실현 가능
장점 |
단점 |
코드 재사용성 증가 |
높은 결합도로 유지보수 어려워질 수 있음 |
다형성 활용한 유연한 설계 가능 |
상속 계층 깊어지면 복잡성 증가 |
🔸 컴포지션(Composition)
- 객체 내부에 다른 객체를 포함(has-a)하여 기능을 위임하는 방법
- 강한 결합도를 낮춰 유연하고 안전한 설계 가능
특징 |
상속 |
컴포지션 |
관계 |
is-a |
has-a |
결합도 |
높음 (강한 결합) |
낮음 (느슨한 결합) |
유연성 |
구조 변경 취약 |
구조 변경 유리 |
다중 역할 |
단일 상속만 가능 |
여러 객체를 조합하여 가능 |
📌 객체지향 설계의 원칙
- 기본적으로 캡슐화를 통해 데이터를 보호
- 가능하면 상속보다 컴포지션을 우선으로 설계
- 추상화, 다형성으로 복잡성을 줄이고, 유지보수와 확장성을 증가
해당 게시글은 학습한 내용을 정리하고 복습하며 정리한 글이므로 틀린 부분이 있을 수 있음.