728x90
OOP 란?
- OOP는 Object Oriented Programming의 줄임말이다.
- 객체 지향 프로그래밍(OOP, Object-Oriented Programming)은 모든 데이터를 객체로 취급하며, 이러한 객체 간의 상호작용을 통해 프로그램을 구성하는 프로그래밍 패러다임이다.
- OOP는 데이터와 그 데이터를 처리하는 메서드를 하나의 단위로 묶어 객체라고 부른다.
OOP 주요 개념
1. 캡슐화 (Encapsulation)
- 객체 안의 데이터(속성)를 숨기고, 그 데이터에 접근할 수 있는 메서드만 제공하는 방식이다.
- 쉽게 말해, 물건을 상자에 넣고 밖에서 직접 상자를 열지 못하게 하고, 버튼을 눌러서 물건을 꺼내는 것과 비슷하다.
왜 캡슐화가 중요한가?
- 데이터 보호: 객체 내부의 중요한 데이터를 외부에서 직접 수정할 수 없게 보호한다.
예를 들어, 속도를 나타내는 speed 값을 마음대로 바꾸는 것을 막을 수 있음 - 안전한 수정: 속성을 수정하려면 setSpeed() 같은 메서드를 사용해야 한다.
이 메서드를 통해서만 유효한 값이 설정되므로, 잘못된 값을 넣을 위험이 줄어든다.
public class Car {
// speed라는 속성은 private으로 선언되어 있어 외부에서 직접 접근할 수 없다.
// 외부에서 speed를 수정하려면 반드시 setSpeed()라는 메서드를 사용해야 한다.
private int speed; // 감춰진 데이터
// 속도를 가져오는 메서드
public int getSpeed() {
return speed;
}
// 속도를 설정하는 메서드
// 속도가 0보다 클 때만 값을 설정하도록 설계되어 있는데 이렇게 하면 음수나 잘못된 값이 들어가는 것을 방지할 수 있다.
public void setSpeed(int speed) {
if (speed > 0) { // 유효한 값일 때만 설정
this.speed = speed;
}
}
public static void main(String[] args) {
Car myCar = new Car();
myCar.setSpeed(100); // 속도를 설정
System.out.println(myCar.getSpeed()); // 속도 출력: 100
}
}
2. 상속 (Inheritance)
- 부모 클래스가 가진 속성과 기능을 자식 클래스가 물려받아 사용할 수 있게 하는 개념이다.
- 자식 클래스는 부모 클래스에서 정의된 메서드나 속성을 그대로 사용하거나, 필요할 때 오버라이드(재정의)하여 자신만의 방식으로 바꿀 수 있다.
왜 상속을 사용할까?
- 코드 재사용: 부모 클래스에서 정의한 기능을 자식 클래스가 재사용할 수 있다.
- 유지보수 용이: 중복되는 코드를 없애고, 한 곳에서만 수정하면 자식 클래스에도 반영되므로 유지보수가 쉬워진다.
// 부모 클래스 Animal
class Animal {
public void sound() {
System.out.println("동물이 소리를 냅니다.");
}
}
// 자식 클래스 Dog
// Dog 클래스는 Animal 클래스를 상속받아 sound() 메서드를 물려받는다.
class Dog extends Animal {
// sound() 메서드를 자신의 방식으로 다시 정의(오버라이드)하여 '강아지가 멍멍!' 소리를 내도록 수정한다.
@Override
public void sound() {
System.out.println("강아지가 멍멍!");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound(); // 강아지가 멍멍!
}
}
3. 다형성 (Polymorphism)
- 하나의 메서드나 클래스가 여러 형태로 동작할 수 있는 것을 의미한다.
- 같은 메서드 이름을 가진 함수가 객체에 따라 다르게 행동하는 것.
- 이 개념은 코드의 유연성과 확장성을 높이는 데 기여한다.
왜 다형성을 사용할까?
- 유연성: 같은 메서드라도 다양한 객체에 맞게 동작을 다르게 구현할 수 있다.
- 확장성: 새로운 클래스가 추가되더라도 기존 코드의 수정 없이 쉽게 통합할 수 있다.
// 부모 클래스 Animal
class Animal {
public void sound() {
System.out.println("동물이 소리를 냅니다.");
}
}
// 자식 클래스 Dog
class Dog extends Animal {
@Override
public void sound() {
System.out.println("강아지가 멍멍!");
}
}
// 자식 클래스 Cat
class Cat extends Animal {
@Override
public void sound() {
System.out.println("고양이가 야옹!");
}
}
public class Main {
public static void main(String[] args) {
// dog과 cat 객체는 Animal 클래스로 선언되었지만, 실제로는 Dog와 Cat 클래스의 인스턴스이다.
Animal dog = new Dog(); // Animal 타입으로 Dog 객체 생성
Animal cat = new Cat(); // Animal 타입으로 Cat 객체 생성
// 객체의 실제 타입에 따라 각기 다른 동작을 수행한다.
dog.sound(); // 강아지가 멍멍! (다형성에 의해 Dog의 sound() 호출)
cat.sound(); // 고양이가 야옹! (다형성에 의해 Cat의 sound() 호출)
}
}
4. 추상화 (Abstraction)
- 복잡한 시스템을 단순화하여, 꼭 필요한 기능만 노출하고 세부적인 구현은 감추는 방법이다.
- 예를 들어, 동물이 소리를 내는 방법을 모두 다르게 구현하더라도, 외부에서는 '소리를 낸다'는 기능만 알면 된다.
즉, 구체적인 동작은 숨기고 그저 '무언가를 한다'는 추상적인 개념만 제공한다.
왜 추상화를 사용할까?
- 복잡성 감소: 불필요한 세부 사항을 숨기고, 중요한 부분만 남겨두어 시스템을 쉽게 이해하고 사용할 수 있다.
- 유연성: 구현 방식은 나중에 구체적으로 정해도 되므로, 더 다양한 구현이 가능하다.
// Animal 클래스는 추상 클래스이며, sound() 메서드를 자식 클래스가 구현하도록 강제한다.
abstract class Animal {
// 추상 메서드: 구현은 자식 클래스가 책임짐
abstract void sound();
}
// Dog와 Cat 클래스는 sound() 메서드를 각각 자신만의 방식으로 구현한다.
// 자식 클래스 Dog
class Dog extends Animal {
@Override
public void sound() {
System.out.println("강아지가 멍멍!");
}
}
// 자식 클래스 Cat
class Cat extends Animal {
@Override
public void sound() {
System.out.println("고양이가 야옹!");
}
}
public class Main {
public static void main(String[] args) {
// 추상화를 통해 복잡한 동작을 감추고, 외부에는 단순한 인터페이스(기능)만 제공한다.
Animal dog = new Dog();
Animal cat = new Cat();
dog.sound(); // 강아지가 멍멍!
cat.sound(); // 고양이가 야옹!
}
}
5. 종속성 역전 (Dependency Inversion)
- 상위 모듈(큰 개념)이 하위 모듈(세부적인 구현)에 의존하는 전통적인 방식에서 벗어나, 하위 모듈이 상위 모듈에 의존하도록 구조를 바꾸는 개념이다.
- 이 방식은 시스템의 유연성과 확장성을 높이는 데 큰 도움을 준다.
왜 종속성 역전을 사용할까?
- 유연한 설계: 구체적인 구현에 의존하지 않고 추상화된 개념에 의존하므로, 새로운 기능을 쉽게 추가하거나 교체할 수 있다.
- 유지보수 용이: 코드 수정이 필요할 때 영향을 최소화할 수 있다.
// 엔진에 대한 인터페이스
interface Engine {
void start();
}
// 구체적인 가솔린 엔진 구현
class GasEngine implements Engine {
public void start() {
System.out.println("가솔린 엔진이 시작됩니다.");
}
}
// 구체적인 전기 엔진 구현
class ElectricEngine implements Engine {
public void start() {
System.out.println("전기 엔진이 시작됩니다.");
}
}
// 자동차 클래스
class Car {
private Engine engine; // Engine 인터페이스에 의존
public Car(Engine engine) {
this.engine = engine;
}
public void startCar() {
engine.start(); // 엔진에 의존하여 차를 시작
}
public static void main(String[] args) {
// 새로운 엔진 타입이 추가되더라도, Car 클래스는 변경할 필요가 없습니다. 단지 새로운 엔진 구현체만 추가하면 된다.
// 종속성 역전을 통해 Car는 엔진의 세부 구현을 알 필요 없이, 다양한 엔진을 쉽게 교체할 수 있다.
Car gasCar = new Car(new GasEngine()); // 가솔린 엔진 사용
Car electricCar = new Car(new ElectricEngine()); // 전기 엔진 사용
gasCar.startCar(); // 가솔린 엔진이 시작됩니다.
electricCar.startCar(); // 전기 엔진이 시작됩니다.
}
}
※ 요약
- 캡슐화: 데이터를 보호하고 메서드를 통해서만 접근하게 한다.
- 상속: 부모가 가진 기능을 자식이 물려받아 사용한다.
- 다형성: 같은 메서드가 다른 객체에서 다르게 동작한다.
- 추상화: 복잡한 부분을 숨기고 필요한 기능만 제공한다.
- 종속성 역전: 하위 모듈(엔진)이 상위 모듈(차)에 의존하도록 설계한다.
728x90
'프로그래밍 기본 개념' 카테고리의 다른 글
Jar 와 War 차이점 (1) | 2024.10.07 |
---|---|
아규먼트(argument) 와 파라미터(parameter) 차이 (0) | 2024.08.02 |
빌드(Build), 배포(Deploy) (0) | 2024.07.31 |
바이너리(Binary)란? (0) | 2024.07.31 |
런타임(Runtime), 컴파일(Compile) (0) | 2024.07.31 |