TIL-2024.04.12 - Basic - OOP. 객체지향프로그래밍(OOP)
질문:
- OOP 가 뭐야?
- 왜 OOP를 사용하는 거야?
1. OOP (Object Oriented Programming) 정의
- 말 그대로, 객체의 관점에서 프로그래밍하는 것을 의미 (자세히 말하자면 클래스는 설계도고 직접일을 하는 구현체는 인스턴스)
- OOP는 객체를 기준으로 코드를 나누어 구현하는데, JAVA의 경우 그 구성 부분 단위가 클래스.
// Person 클래스 정의
public class Person {
// 필드(속성)
private String name;
private int age;
// 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 메서드
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
// Getter 메서드
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
// 객체 생성 및 사용
public class Main {
public static void main(String[] args) {
Person person1 = new Person("Janku", 30);
person1.introduce(); // "Hello, my name is Janku and I am 30 years old."
System.out.println("Name: " + person1.getName()); // "Name: Janku"
System.out.println("Age: " + person1.getAge()); // "Age: 30"
}
}
갑작스러운 JAVA예제이지만, 위의 예제를 살펴보면,
1) Person Class 정의
- Person 클래스는 name과 age라는 private 필드 가짐.
- Person 클래스의 생성자 Person(String name, int age)는 객체를 초기화할 때 name과 age를 설정.
- introduce() 메서드는 객체의 정보를 출력.
- getName()과 getAge() 메서드는 private 필드인 name과 age의 값을 외부에서 읽을 수 있게함.
2) 객체 생성 및 사용
- Main 클래스의 main 메서드에서 Person 클래스의 인스턴스인 person1 객체를 생성.
> 이때, 생성자를 호출하여 name을 "John", age를 30으로 초기화. - person1.introduce()를 호출해 Hello, my name is John and I am 30 years old."라는 메시지가 출력.
- person1.getName()과 person1.getAge()를 통해 name과 age의 값을 각각 "John"과 30으로 출력합니다.
2. OOP 사용 이유
- 기존 C 언어들은 절차 지향
- 절차 지향으로 해당 코드를 다시 작성하면, 아래와 같음
// Person 클래스의 필드(속성)
private static String name;
private static int age;
// Person 객체 생성 및 사용
public class Main {
public static void main(String[] args) {
// Person 객체의 필드 초기화
name = "John";
age = 30;
// introduce() 메서드 호출
introduce(); // "Hello, my name is John and I am 30 years old."
// getName()과 getAge() 함수 호출하여 정보 출력
System.out.println("Name: " + getName()); // "Name: John"
System.out.println("Age: " + getAge()); // "Age: 30"
}
// introduce() 함수 정의
public static void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
// getName() 함수 정의
public static String getName() {
return name;
}
// getAge() 함수 정의
public static int getAge() {
return age;
}
}
- 위의 코드와 같이 절차지향 프로그래밍으로 작성한 경우, 컴파일이나 런타임에 문제는 없음.
- 만약 다른 사람을 넣고 싶으면? (100명 정도) > 사람에 따라 새롭게 작성을 해줘야함 * 100.
- 만약 100명 넣은 코드를 바꿔야하면 ? > 새롭게 작성된 코드를 바꿔야함 * 100
- 이 말은, 해당 코드는 재사용이 불가 & 유지 보수 최악 의미.
- OOP를 사용하면, 이런 행위를 최소한으로 할 수 있음
... 생략 ...
Person person1 = new Person("Janku", 30);
person1.introduce(); // "Hello, my name is Janku and I am 30 years old."
Person person2 = new Person("Jason", 12);
person1.introduce(); // "Hello, my name is Jason and I am 12 years old."
... 생략 ...
3. OOP 특징
1) 캡슐화, Encapsulation : 객체의 데이터와 메서드를 묶음 (데이터와 메서드를 객체 하나로 묶고, 외부에서 직접 접근을 제어)
- 캡슐화는 데이터와 그 데이터를 처리하는 메서드를 하나로 묶음
- 이를 통해 객체의 내부 구현을 외부로부터 감추고 객체 간의 상호 작용은 정의된 인터페이스를 통해서만 가능.
public class Person {
private String name;
private int age;
// 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter 메서드
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Setter 메서드 (내부 데이터 변경을 허용하는 경우)
public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
}
// 위의 예제에서 Person 클래스는 name과 age라는 필드를 private으로 선언하여 외부에서 직접 접근 불가
// 대신에 getName()과 getAge() 메서드를 통해 외부에서 필드 값을 읽음.
// 이렇게 하면 내부 데이터에 대한 접근을 제어하고, 데이터 유효성을 보장.
2) 은닉화, Information hiding : 필요한 녀석만 보기
- 은닉화는 캡슐화의 목표로, 객체의 내부 구현 세부 사항을 외부로부터 숨기는 것을 의미.
- 객체의 내부 상태는 private으로 선언하고, 외부에서 접근 가능한 메서드를 통해 데이터에 접근.
public class BankAccount {
private double balance; // 은행 계좌 잔액
// 입금 메서드
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// 출금 메서드
public void withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
}
}
// 잔액 조회 메서드
public double getBalance() {
return balance;
}
}
// 위의 예제에서 BankAccount 클래스는 balance 필드를 private으로 선언하여 외부에서 직접 접근 불가.
// 대신에 deposit(), withdraw(), getBalance() 메서드를 통해 입금, 출금, 잔액 조회 기능을 제공.
// 이렇게 하면 잔액을 직접 조작하는 것을 외부로부터 숨김.
3) 상속, Inheritance : 재정의
- 상속은 기존 클래스(부모 클래스)의 속성과 메서드를 그대로 물려받아 새로운 클래스(자식 클래스)를 생성.
- 자식 클래스는 부모 클래스의 기능을 확장하거나 수정하여 재사용.
public class Animal {
public void makeSound() {
System.out.println("Animal sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark bark!");
}
}
// 위의 예제에서 Animal 클래스는 makeSound() 메서드 지님.
// Dog 클래스는 Animal 클래스를 상속받았으며, makeSound() 메서드를 재정의하여 "Bark bark!"를 출력.
4) 추상화, Abstraction: 공통된 속성이나 기능을 묶음 (현실 세계의 개념을 간추려서 모델링하고 클래스로 표현)
- 추상화는 공통의 속성이나 기능을 묶어 이름을 붙이는 것을 의미.
- 추상화를 통해 객체의 중요한 특징에만 초점을 맞추고 불필요한 세부 사항을 숨김.
public abstract class Shape {
public abstract double calculateArea();
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// 위의 예제에서 Shape 클래스는 도형의 추상적인 개념을 나타내며, calculateArea() 메서드를 추상 메서드로 선언.
// Circle 클래스는 Shape 클래스를 상속받아 원의 면적을 구하는 기능을 구현.
5) 다형성, Polymorphism: 딴말하기 (Overload & Override)
- 다형성은 같은 이름의 메서드가 서로 다른 클래스에서 다르게 동작.
- 다형성을 통해 코드의 유연성과 재사용성을 높임.
public class Animal {
public void makeSound() {
System.out.println("Animal sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark bark!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
// 위의 예제에서 Animal 클래스의 makeSound() 메서드는 Dog 클래스와 Cat 클래스에서 각각 다르게 재정의되어 동작.
// 이렇게 하면 동일한 메서드 이름으로 다양한 객체를 처리 가능
답변
- OOP 가 뭐야?
- OOP는 객체지향-프로그래밍으로 현실 세계를 추상화하여 데이터와 해당 데이터를 처리하는 메서드를 하나의 객체로 묶은 기법
- 좀 더 쉽게, 객체 위주로 (객체를 만들어) 코드를 짜는 기법 - 왜 OOP를 사용하는 거야?
- 조금 복잡하더라도, 한번 제대로 짜두면 높은 재사용성과 유지보수 측면에서 이득 !