객체지향프로그래밍(2) - 객체 지향의 핵심 4가지
1. 추상화(Abstraction)
객체를 단순화하고 필요한 속성과 동작에 중점을 두는 개념. 복잡한 시스템을 모델링하거나 특정 개념을 이해하기 쉽게 만들기 위해 객체를 추상화한다. 클래스를 정의하고 해당 클래스의 인스턴스를 생성하는 것이 모두 이 추상화를 위해서 이루어진다고 이해했다.
2. 상속(Inheritance)
클래스 간의 계층적인 관계를 형성하는 개념. 기존 클래스의 특성을 다른 클래스가 상속받아 확장하거나 재사용하는 방식을 제공한다.
부모클래스(상위 클래스)의 속성과 메서드를 자식 클래스(하위 클래스)가 상속받아 사용할 수 있다. 이를 통해 코드의 재사용성을 높이고 유지보수를 용이하게 할 수 있다.
※ 오버라이딩
- 자식 클래스에서 부모 클래스의 메서드를 재정의하는 개념. 자식 클래스는 동일한 이름의 메서드를 정의하여 부모 클래스의 메서드를 오버라이딩할 수 있다. 자식 클래스에서 부모 클래스의 메서드를 오버라이딩하면 부모 클래스의 동작을 대체하게 된다.
- 부모 클래스의 동작을 변경하고 싶을 때는 그냥 해당 메서드를 재정의 하면 되는데, 만약 부모 클래스의 동작은 유지하면서 거기에 좀 더 덧붙이고 싶다면? 즉, 확장하고 싶다면? → super를 사용하면 된다!
super를 앞에 붙인 상태로 부모 클래스의 메서드를 호출하고, 그 후에 추가적인 동작을 구현. 이를 통해 부모 클래스의 동작을 유지하면서 확장된 동작을 구현할 수 있다.
class Person:
def __init__(self, name):
self.name = name
def say_hello(self):
print(f"안녕하세요, 저는 {self.name}입니다.")
class Student(Person):
def __init__(self, name, school):
super().__init__(name) # 부모 클래스의 __init__ 메서드 호출
self.school = school
def say_hello(self):
super().say_hello() # 부모 클래스의 say_hello 메서드 호출
print(f"저는 {self.school} 학교에 다니고 있습니다.")
# 객체 생성
student = Student("Alice", "ABC High School")
# 메서드 호출
student.say_hello()
위와 같이 코드를 작성하면 say_hello 메서드를 호출했을 때
안녕하세요, 저는 Alice입니다.
저는 ABC High School 학교에 다니고 있습니다.
이러한 결과가 출력되게 될 것이다.
3. 다형성(Polymorphism)
동일한 메서드가 클래스에 따라 다르게 행동할 수 있다는 개념. 이를 통해 객체의 타입에 상관없이 일관된 인터페이스를 사용할 수 있고, 유연하고 확장 가능한 코드를 작성할 수 있다. 오버로딩이나 오버라이딩 개념에서도 다형성을 엿볼 수 있다. 여러 클래스가 같은 인터페이스를 구현한다? → 아래 코드를 참고하기
# 동물 인터페이스
class Animal:
def sound(self):
pass
# 개 클래스
class Dog(Animal):
def sound(self):
return "멍멍!"
# 고양이 클래스
class Cat(Animal):
def sound(self):
return "야옹!"
# 동물 소리 출력 함수
def make_sound(animal):
print(animal.sound())
# 다형성 활용
dog = Dog()
cat = Cat()
make_sound(dog) # 개의 소리 출력: 멍멍!
make_sound(cat) # 고양이의 소리 출력: 야옹!
※오버로딩
같은 이름을 가진 메서드를 여러 개 정의하고, 매개변수의 타입 또는 개수에 따라 다른 동작을 수행하는 개념. 참고로, 파이썬은 지원하지 않는다.(함수나 메서드 내에서 조건문을 사용하여 매개변수에 따라 다른 동작을 구현할 수는 있겠다.)
4. 캡슐화(Encapsulation)
객체의 일부 구현 내용에 대해 외부로부터의 직접적인 액세스를 차단하는 개념. 클래스는 속성(데이터)과 메서드(동작)를 캡슐화하여 객체의 상태와 동작을 외부로부터 감추고, 필요한 경우에만 접근을 허용할 수 있다.
이를 통해 객체의 내부 구현 세부사항은 숨기고, 외부에서는 인터페이스를 통해 객체와 상호작용할 수 있다.
클래스 내부의 데이터와 메서드는 보통 세가지 접근 제한자(access modifier)를 사용하여 접근 수준을 설정할 수 있다.
- public access modifier
- 언더바없이 시작하는 속성이나 메서드
- 외부에서 자유롭게 접근할 수 있다. - protected access modifier
- 언더바 1개로 시작하는 속성이나 메서드
- 해당 클래스와 해당 클래스를 상속받은 클래스 내부에서 접근할 수 있다. 즉, 외부에서 직접적 접근은 불가능하고, 상속 관계에서는 접근이 가능하다. - private access modifier
- 언더바 2개로 시작하는 속성이나 메서드
- 해당 클래스 내부에서만 접근할 수 있다. 외부에서의 직접적인 접근은 허용되지 않는다.
class MyClass:
public_var = 10
_protected_var = 20
__private_var = 30
def public_method(self):
print("This is a public method.")
def _protected_method(self):
print("This is a protected method.")
def __private_method(self):
print("This is a private method.")
# 클래스 외부에서 접근 가능한 멤버
obj = MyClass()
print(obj.public_var) # Output: 10
obj.public_method() # Output: This is a public method.
# 상속 관계에서 접근 가능한 멤버
class SubClass(MyClass):
def access_protected(self):
print(self._protected_var) # Output: 20
self._protected_method() # Output: This is a protected method.
sub_obj = SubClass()
sub_obj.access_protected()
# 클래스 내부에서만 접근 가능한 멤버
print(obj.__private_var) # Error: 'MyClass' object has no attribute '__private_var'
obj.__private_method() # Error: 'MyClass' object has no attribute '__private_method'