데이터과학 유망주의 매일 글쓰기 — 42일차
상속자들(Inheritors)
# 상속, #포함, #객체지향
오늘 한일:
오늘은 프로그래밍의 꽃으로 여겨지는 객체지향언어(Object Oriented Programming)의 개념에 대해 배웠다. 그중에서도 상속(Inheritance) 및 포함(Composition)이라는 개념이 매우 중요하게 느껴졌는데, 오늘 대부분의 시간을 이 개념을 익히기 위해 쓴만큼, 오늘은 이 개념들을 설명하는 글을 쓰고자한다.
내가 자주 이용하는 RealPython이라는 웹사이트의 Inheritance and Composition: A Python OOP Guide라는 글과, W3School의 Python Inheritance라는 글, 그리고 GeeksforGeeks라는 사이트의 Inheritance and Composition in Python라는 글을 통해 개념을 공부했다.
1. 개념정리
상속과 포함은 객체지향 프로그래밍(Object-Oriented Programming)에서 가장 중요한 두 가지 컨셉이다. 이 둘은 두 개의 클래스 사이에 있는 관계를 모델링한다. 객체 지향 설계의 기본적인 요소이며, 프로그래머들이 재사용 가능한 코드를 쓸 수 있도록 한다. 또한, 새로운 기능을 더하거나 이미 있는 기능을 변경할 때, 어플리케이션의 셜계가 어떻게 바뀌어야하는지를 결정한다. 다만, 서로 코드를 재사용 가능하게 하는 방법에 있어 구분된다.
상속(Inheritance)
상속은 “~는 ~이다(is a)”라고 정의될 수 있는 관계를 모델링한다. 즉, 본래 클래스에서 유래된 클래스가 있다면, “유래된 클래스는 본래 클래스의 ‘더 특화된 버전’이다.”라고 할 수 있다.
상속 클래스는 아래처럼 단일화된 모델링 언어(Unified Modeling Language, UML)를 나타낸다.
상속관계는 아래와 같은 특징을 가진다.
- 서로 다른 것에서 상속받는 클래스는 “유래된 클래스(derived classes)”, “서브클래스(subclasses)”, “서브 타입(subtypes)” 등으로 불린다.
- 다른 클래스가 유래되는 클래스들은 “본래 클래스(base classes)” 또는 “슈퍼 클래스(super classes)”라고 불린다.
- 유래된 클래스는 본래 클래스에서 유래되거나, 상속되거나, 확장된다고 볼 수 있다.
동물(Animal)이라는 클래스가 있다고 가정하자, 여기서 말(Horse)이라는 클래스를 만든다고 하자. 상속관계는 “Horse은 Animal이다”라고 정의한다. 이는 Horse가 Aniaml의 인터페이스와 구조를 상속받는다는 뜻이며, Horse라는 객체가 Animal이라는 객체를 어플리케이션상에서 대체할 수 있다는 뜻이다.
이는 Liskov 대체 원리라고도 한다. 이 원리는 “T가 S의 서브타입이면, 프로그램의 그 어떤 속성(properties)도 고치지 않은 상태에서, T타입의 객체는 S타입의 객체로 대체될 수 있다.”라고 정의한다.
포함(composition)
포함은 “가 ~를 가지고 있다.”라는 관계를 정의한다. 서로 다른 타입의 객체를 결합하여 하나의 복잡한 객체를 생성할 수 있게 한다. 이는 Composite(포함하는)이라는 클래스가 Component(부분)라는 클래스를 담을 수 있다는 뜻이다. “Composite은 Component를 가진다.”라는 관계이다.
차원은 아래와 같은 방법으로 표현된다.
- 숫자: Composite이 가지고 있는 Component instance의 개수를 나타낸다.
- *: Composite 클래스가 다수의 Component Instances를 가질 수 있는지를 나타낸다.
- 1..4 범위: Composite 클래스가 Component instance의 범의를 가질 수 있는지를 나타낸다. 범위는 인스턴스의 최소 및 최대 개수를 통해 나타내거나, 1..*에서의 최소 및 다수의 인스턴스 개수로 나타낸다.
다른 클래스의 객체를 가질 수 있는 클래스는 Composite이라 불리며, 여기서 더 복잡한 타입을 생성할 수 있는 클래스는 component라고 불린다.
예를 들어, Horse 클래스가 Tail이라는 타입의 객체로 생성될 수 있다고 가정하자. 포함관계에서는 “Horse가 Tail을 가지고 있다.”라고 표현할 수 있다.
포함은 인터페이스나 구조를 상속하는 상속관계와는 다르게, 하나의 객체에 다른 객체를 더하는 방법으로 코드를 재사용할 수 있게 해준다. Horse와 Dog라는 클래스는 Tail이라는 기능을 본래 클래스에서 상속 받기보다, 포함관계를 통해 다르게 가져갈 수 있다.
2. 예제 탐구
상속(Inheritance)
예) 각각 머리크기와 꼬리의 길이를 나타내는 head와 tail이라는 속성을 가진 Horse라는 본래 클래스를 정의하고, says라는 method를 통해 “neigh”라는 단어를 출력하라. 또한 Pony라는 유래 클래스를 정의하고, 본래 클래스의 속성을 상속하라. 마지막으로, 유래 클래스를 이용하여 객체를 만들고, says method를 실행하라.
# 본래 클래스 정의class Horse: def __init__(self, head, tail): self.headsize = head self.tailsize = tail print("This is a Horse")
def says(self): print("It has a {} and a {}, who neighs!".format(self.headsize, self.tailsize))# 본래 클래스를 사용하여 객체를 생성하고, says method를 실행한다x = Horse("big head", "long tail")x.says()
# 유래 클래스 정의class Pony(Horse): pass print("This is a Pony")# 유래 클래스를 사용하여 객체를 생성하고, says method를 실행한다x = Pony("small head", "short tail")x.says()
포함(Composition)
예) Horse라는 Composite 클래스와 Pony라는 Component 클래스를 정의하라. Pony클래스는 says1이라는 method를 사용한다. Horse 클래스 안에서 Component 클래스의 객체를 생성하고, says2라는 method를 사용한다. Horse 클래스는 또한, Component 클래스의 method를 불러온다. 마지막으로, Composite 클래스를 바탕으로 객체를 생성하고 method를 불러온다.
class Pony: # Composite class constructor def __init__(self, head, tail): self.headsize = head self.tailsize = tail print("This is a Pony") # Composite class instance method def says1(self): print("Pony has a {} and a {}, who neighs!".format(self.headsize, self.tailsize))class Horse: # Composite class constructor def __init__(self, head, tail): self.headsize = head self.tailsize = tail print("This is a Horse") # Component class 객체 생성 self.pony = Pony("small head", "short tail") # Composite class instance method def says2(self): print("Horse has a {} and a {}, who neighs!".format(self.headsize, self.tailsize)) # Component 클래스의 says method 불러오기 self.pony.says1()# Composite 클래스의 객체 생성horse = Horse("big head", "long tail")# Composite 클래스의 says method 불러오기horse.says2()
앞으로 할일:
사실 상속과 포함의 개념을 좀 더 잘 이해하기 위해, 글을 쓸 목적으로 여러자료를 찾아보고 공부했으나, 아직 나도 확실하게 이해했는지 확신이 서지 않는 것이 사실이다. 일단 지금 이해하기로는, “상속을 통해 유래 클래스는 본래 클래스의 속성 자체를 가져올 수 있고, 포함을 통해서는 Compo컴포넌트 클래스의 특정한 객체나 method를 가져올 수 있다.”라는 것으로 느껴진다. 상속관계에서는 유래 클래스는 본래 클래스를 완전히 대체할 수 있다. 포함관계에서는 Composite 클래스가 Component 클래스의 객체나 method를 불러오기 때문에 서로가 서로를 완전히 대체할 수는 없지만, Composite 클래스가 필요한 만큼 하나의 Component의 속성을 가져다 쓸 수 있다.
부디 내가 이해한 것이 맞기를 바란다. 그렇지 않으면, 검사받고 수정을 해야하니까. 내가 오늘 수 시간 동안 이 개념을 이해하기 위해 노력한 것이 헛수고가 아니기를 바란다.
이 개념은 앞으로 객체지향언어인 Python을 이용하여, 좀 더 심화된 개발을 하기 위해 반드시 짚고 넘어가야하는 개념이다. 사실 프로그래밍을 통해 자신이 원하는 프로그램을 효율적으로 개발하고 싶은 이들은 모두 알아야하는 개념이기도 하다. 프로그래밍 세계에서는 Don’t Repeat Yourself(DRY)라는 개념이 매우 중요한데, 한번 쓴 코드를 반복해서 쓰는 것을 피하자는 모토이다. 반복적인 코드는 비효율적이며, 프로그래밍의 코어 목적인 “자동화"에 반하는 형태이기 때문이다.
이 개념을 잘 익혀서, 앞으로 3주 후에 하게될 프로젝트에서 유용하게 사용하고 싶다. 물론, 그 이후로도 데이터 분석 프로그램을 개발해야 하거나 할 때에도 매우 유용한 도구가 될 것 같은 느낌이다. 모든 배움에는 굴곡이 있고, 어려움이 있지만, 충분히 시간을 들이면 노력은 배신하지 않는다고 생각한다.
한편으로는, 오히려 우리가 이런 것들을 배울 수 있다는 것이 감사하다. 이렇게 프로그래밍을 손쉽게 할 수 있는 도구를 우리의 선배들이 정리하지 않았다면, 지금 이렇게 프로그래밍을 배우는 것이 훨씬 더 어려울 수 있으니까. 그들이 남긴 것을 잘 상속받아서 좋은 것을 만드는 것이 우리가 할일 같다. 부모에게 재산을 상속받을 때 처럼 형제 자매와 싸울 필요가 없다는 것이 상당히 감사하다.
부디 이번 주 안에는 객체지향의 중심 개념들을 확실히 잡고 넘어갈 수 있기를 나를 포함한 모든 동료들을 위해 바래본다.
참조: