Github Repository : https://github.com/WE-Learning-CS/OOP/tree/main/01_OOP
객체지향프로그래밍은 시스템을 컴퓨터 명령어의 목록으로 구성하지 않고 여러 다양한 객체들의 집합으로 구성하는 것을 말한다. 프로그램을 객체(object)라는 기본 단위로 나누고 객체들의 상호작용으로 기능들을 구현하는 방식이다.
객체지향프로그래밍은 개발자들이 조작하기 위해 필요한 로직보다는 조작하고자 하는 객체에 초점을 맞추기때문에 규모가 크고 복잡하며 능동적으로 업데이트되거나 유지되는 프로그램에 적합하다.
핵심
객체지향은 필요에 의해서 생겨났다. 객체지향 이전에는 기능 구현을 위한 로직을 처음부터 끝까지 나열하는 방식으로 소스코드 파일의 위에서부터 아래로 실행한 후 마지막 결과를 나타내는 단순한 구조의 프로그래밍을 주로 사용했다.
구조가 단순하다보니 프로그래밍을 하기에는 쉬웠지만 전체적인 구조가 정리되지 않고 유지보수의 난이도가 높아졌다. 코드에는 기능구현만을 위한 로직이 존재하고 구조가 존재하지 않으니 코드가 서로 꼬이는 스파게티 코드
가 되는 경우가 자주 발생했다.
이런 문제를 해결하기 위해 나온 것이 로직에 구조를 더한 객체지향프로그래밍이다.
핵심 이유
객체지향의 장단점으로는 다음과 같다.
장점
1. 코드의 재사용이 용이하다. 코드는 상속을 통해 재사용이 가능하다. 즉 같은 코드를 여러번 작성할 필요가 없다.(생산력도 높아진다)
2. 모듈화, 캡슐화로 유지보수가 쉽다
3. 캡슐화 및 추상화를 사용하여 복잡한 코드를 숨기고 보호가 가능하기때문에 보안면에서 좋다.
단점
1. 객체가 많으면 용량이 증가한다. 속도가 상대적으로 느리다.
2. 코드를 이해하기 쉽게 만드는 설계 과정에서 많은 시간과 노력이 필요하다.
캡슐화는 연관있는 변수와 함수를 하나의 단위로 묶는 것을 말하며 모든 중요한 정보가 객체 내부에 포함되어 있고, 선택된 정보만 노출된다는 것을 말한다. 이를 통해 내부의 기능 구현이 변경되더라도 그 기능을 사용하는 코드는 영향을 받지 않도록 만들어준다. 즉, 내부 구현 변경의 유연함을 주는 것이 캡슐화이다.
다음은 캡슐화 예시 코드이다.
class Car:
def __init__(self, model, color, year):
self.model = model
self.color = color
self.year = year
def drive(self, speed):
if speed != 0:
return f'The speed of the car is {speed}.'
def stop(self):
return "The car has stopped."
자동차와 관련된 변수와 함수가 묶여있는 클래스 자체가 캡슐화의 예시라고 할 수 있다.
Tell, Don't Ask
데이터를 물어보지 않고, 기능을 실행해 달라고 말하라는 규칙
데미테르의 법칙
메서드에서 생성한 객체의 메서드만 호출
파라미터로 받은 객체의 메서드만 호출
필드로 참조하는 객체의 메서드만 호출
보통 java
에서 메소드의 공개 범위를 access modifier(접근 제어자)
로 결정한다.
public
: 해당 클래스의 객체, 상속받는 클래스의 객체, 그리고 외부 객체 모두 다 접근 가능private
: 해당 클래스의 객체 내부에서만 접근 가능, 상속받는 sub class도 직접적인 접근은 할 수 없고 상속받지 않는다.protected
: 외부 객체는 접근할 수 없다. 상속받는 sub class는 접근 할 수 있고 상속받을 수 있다.파이썬에서는 위와 같은 접근제어자를 직접적으로 제공하지 않는다.
파이썬에서의 protected는 _(single underscore)
를 통해 구현할 수 있다.
실제 제약되지는 않고 일종의 경고 표시로 사용된다.
class Car:
def __init__(self, name, price):
self.name = name
self._price = price
파이썬에서는 __(double underscore)
를 통해 private를 구현할 수 있다.
class Car:
def __init__(self, name, price):
self.name = name
self.__price = price
price 필드를 비공개 속성으로 만들었기 때문에 외부에서 접근 시 위와같은 에러가 발생한다.
비공개 속성에 접근할 수 있는 방법은 java와 마찬가지로 클래스 안의 메서드를 통해 접근이 가능하다.
--> private 생성자를 만들고 getter, setter를 통해서 접근하는 것과 마찬가지.
class Car:
def __init__(self, name, price):
self.name = name
self.__price = price
def discount(self, percent):
self.__price = self.__price - (self.__price * percent)
return f'discounted price = {self.__price}'
메서드도 __(double underscore)
를 통해 비공개로 만들 수 있다.
class Person:
def __greeting(self):
print("hello")
def hello(self):
self.__greeting()
출처 : JavaScript ES7 OOP. Abstraction. Class. #1 - YouTube
youtube.com
추상화는 데이터나 프로세스 등 의미가 비슷한 개념이나 표현으로 정의하는 과정이다.
파이썬에서 추상클래스를 구현하기 위해서는 반드시 abc
모듈을 import 해야한다.
from abc import *
class AbstractClass(metaclass=ABCMeta):
@abstractmethod
def method1(self):
pass
추상 메소드는 생략하면 기본적인 클래스 기능은 동작하지만 추상 메소드를 추가한 후에 객체를 생성하면 에러가 발생한다.
상속은 자식클래스(Sub Class)가 부모클래스(Base Class, Super Class)로부터 Property와 Method들을 상속받을 수 있는 특성을 말하며 한 타입을 그대로 사용하면서 구현을 추가할 수 있도록 해주는 방법을 제공한다.
class Car:
def __init__(self, model, color, year):
self.model = model
self.color = color
self.year = year
def drive(self, speed):
if speed != 0:
return f'The speed of the car is {speed}.'
def stop(self):
return "The car has stopped."
class Truck(Car):
def __init__(self, name):
self.name = name
def load(self, baggage):
return f"Loaded {baggage} into the truck."
위와 같이 Car class를 상속받은 Truck class가 있다고 가정했을때 Truck class에는 정의되어 있지 않지만 다음과 같이 Car class에 정의되어 있던 drive, stop 메소드를 사용할 수 있다. 또한 상속받은 클래스의 기능 뿐 아니라 새로운 기능도 추가할 수 있다.
truck = Truck("Poter")
truck.drive(100) # The speed of the car is 100.
truck.stop() # The car has stopped.
truck.load("box") # Loaded box into the truck.
다형성은 한 객체가 여러가지(Poly) 모습(morph)을 갖는다는 것을 의미한다. 여기서 모습이란 타입을 뜻하는데, 즉 다형성은 한 객체가 여러가지 타입을 가질 수 있다는 것을 뜻한다.
클래스의 다형성을 보기 전에, 우리는 len()
메소드가 다음과 같이 여러 모습으로 사용되는 것을 이미 알고있다.
출처 : https://www.programiz.com/python-programming/polymorphism
len()
은 str, list, dict와 같은 많은 데이터 유형에 쓰여질 수 있다.
파이썬은 다른 클래스가 같은 이름의 메서드를 가질 수 있도록 허용한다. 이도 다형성의 한 예이다.
class Car:
def __init__(self, model, color, year):
self.model = model
self.color = color
self.year = year
def drive(self, speed):
if speed != 0:
return f'The speed of the car is {speed}.'
def stop(self):
return "The car has stopped."
class Motorcycle:
def __init__(self, model, color, year):
self.model = model
self.color = color
self.year = year
def drive(self, speed):
if speed != 0:
return f'The speed of the motorcycle is {speed}.'
def stop(self):
return "The motorcycle has stopped."
또한 overiding
처럼 부모 클래스의 메소드가 자식 클래스에서 변형되어 다양한 기능으로 나타날 수 있는것도 한 예시이다.
참고로 overloading
처럼 이름은 같지만 인수가 다른 여러 메서드를 만드는 방법은 python에서는 지원하지 않는다
참고
object-oriented programming (OOP)
class 정리 - 추상클래스(abstract class)
Polymorphism in Python