TIL_27 : 추상화, 캡슐화
🙄 객체 지향 프로그래밍의 4가지 기둥
-
추상화
-
캡슐화
-
상속
-
다형성
🙄 추상화
➡ 추상화란?
- 프로그래머들이 특정 코드를 사용할 때 필수적인 정보를 제외한 세부사항을 가리는 것
- 변수 정의, 함수, 클래스를 사용하는 것도 추상화
👉 변수에 한번 값을 설정하면 그 이후 값을 몰라도 이름만으로 사용 가능
👉 함수의 이름, 파라미터, 기능만 알면 구체적인 동작 방식은 몰라도 잘 사용 가능
➡ 추상화 잘하기
- 추상화를 잘 적용하면 본인이 만든 클래스를 동료가 잘 사용할 수 있고 반대로 동료가 만든 클래스도 내가 잘 사용 가능
- 본인이 만든 것을 시간이 지나 다시 사용할 때도 잘 사용 가능
이름 잘 짓기
- 클래스, 메소드, 변수 이름을 그 의미가 잘 담기게 지어야 함
- 어디서 어떻게 사용할지 직관적으로 알 수 있다
문서화하기
- 항상 이름에 모든 의미를 담기에는 한계가 존재
- 문서화 = docstring : documentation string, 문서화 문자열
""" docstring """
를 이용해 작성- 설명하고 싶은 클래스나 메소드 이름 바로 아래 써준다
class BankAccount:
"""은행 계좌 클래스"""
interest = 0.02
def __init__(self, owner_name, balance):
"""인스턴스 변수 : name(문자열), balance(실수형)"""
self.owner_name = owner_name
self.balance = balance
def deposit(self, amount):
"""잔액 인스턴스 변수 balance를 파라미터 amount만큼 늘려주는 메소드"""
self.balance += amount
def withdraw(self, amount):
"""잔액 인스턴스 변수 balance를 파라미터 amount만큼 줄여주는 메소드"""
if self.balance < amount:
print("Insufficient balance!")
else:
self.balance -= amount
def add_interest(self):
"""잔액 인스턴스 변수 balance를 이자율만큼 늘려주는 메소드"""
self.balance *= 1 + BankAccount.interest
type hinting
- 정적 언어처럼 type을 표시할 수 있는 기능
변수이름: type
- return 값의 type : 함수 정의 부분 뒤에
-> type
작성
class BankAccount:
"""은행 계좌 클래스"""
interest: float = 0.02
def __init__(self, owner_name: str, balance: float) -> None:
"""인스턴스 변수 : name(문자열), balance(실수형)"""
self.owner_name = owner_name
self.balance = balance
def deposit(self, amount: float) -> None:
"""잔액 인스턴스 변수 balance를 파라미터 amount만큼 늘려주는 메소드"""
self.balance += amount
def withdraw(self, amount: float) -> None:
"""잔액 인스턴스 변수 balance를 파라미터 amount만큼 줄여주는 메소드"""
if self.balance < amount:
print("Insufficient balance!")
else:
self.balance -= amount
def add_interest(self) -> None: # return 값이 없으므로 None
"""잔액 인스턴스 변수 balance를 이자율만큼 늘려주는 메소드"""
self.balance *= 1 + BankAccount.interest
print(BankAccount.__doc__)
print()
help(BankAccount)
# 은행 계좌 클래스
# Help on class BankAccount in module __main__:
#
# class BankAccount(builtins.object)
# | BankAccount(owner_name: str, balance: float) -> None
# |
# | 은행 계좌 클래스
# |
# | Methods defined here:
# |
# | __init__(self, owner_name: str, balance: float) -> None
# | 인스턴스 변수 : name(문자열), balance(실수형)
# |
# | add_interest(self) -> None
# | 잔액 인스턴스 변수 balance를 이자율만큼 늘려주는 메소드
# |
# | deposit(self, amount: float) -> None
# | 잔액 인스턴스 변수 balance를 파라미터 amount만큼 늘려주는 메소드
# |
# | withdraw(self, amount: float) -> None
# | 잔액 인스턴스 변수 balance를 파라미터 amount만큼 줄여주는 메소드
# |
# | ----------------------------------------------------------------------
# | Data descriptors defined here:
# |
# | __dict__
# | dictionary for instance variables (if defined)
# |
# | __weakref__
# | list of weak references to the object (if defined)
# |
# | ----------------------------------------------------------------------
# | Data and other attributes defined here:
# |
# | __annotations__ = {'interest': <class 'float'>}
# |
# | interest = 0.02
# Process finished with exit code 0
🙄 캡슐화
➡ 캡슐화란?
- 객체의 일부 구현 내용에 대한 외부로부터의 직접적인 액세스를 차단 하는 것
- 객체의 속성과 그것을 사용하는 행동을 하나로 묶는 것
➡ 캡슐화의 필요성
- 아래 예시 주석으로 설명
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.age = age
self.resident_id = resident_id
def authenticate(self, id_field):
"""본인이 맞는지 확인하는 메소드"""
return self.resident_id == id_field
def can_drink(self):
"""음주 가능 나이인지 확인하는 메소드"""
return self.age >= Citizen.drinking_age
def __str__(self):
"""주민 정보를 문자열로 리턴하는 메소드"""
return self.name + "씨는 " + str(self.age) + "살입니다!"
jahyeon = Citizen("구자현", 24, "12345678")
young = Citizen("민정호", 5, "98765432")
print(jahyeon.resident_id) # 12345678, 주민등록번호 유출 위험
young.age = -5 # 나이를 마음대로 변경 가능
print(young) # 민정호씨는 -5살입니다!
young.age = 20
print(young.can_drink()) # True, 음주 가능 나이로 조작 가능
➡ 객체 내부를 숨기는 법
- 변수나 메소드 앞에
__
를 붙이면 클래스 밖에서 접근이 불가능
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.__age = age
self.__resident_id = resident_id
def __authenticate(self, id_field):
"""본인이 맞는지 확인하는 메소드"""
return self.__resident_id == id_field
def can_drink(self):
"""음주 가능 나이인지 확인하는 메소드"""
return self.__age >= Citizen.drinking_age
def __str__(self):
"""주민 정보를 문자열로 리턴하는 메소드"""
return self.name + "씨는 " + str(self.__age) + "살입니다!"
jahyeon = Citizen("구자현", 24, "12345678")
print(jahyeon.__resident_id)
# AttributeError: 'Citizen' object has no attribute '__resident_id'
print(jahyeon.__authenticate("12345678"))
# AttributeError: 'Citizen' object has no attribute '__authenticate'
➡ 객체의 메소드를 통해 변수 접근하기 #1
__age
,__resident_id
는 클래스 밖에서 직접 접근 불가- 주민의 나이를 알 수 없고 수정할 수도 없다
- 무조건 숨기기만 해서는 안됨.
👉 접근할 수 있는 메소드를 따로 만듦
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.__age = age
self.__resident_id = resident_id
def authenticate(self, id_field):
"""본인이 맞는지 확인하는 메소드"""
return self.__resident_id == id_field
def can_drink(self):
"""음주 가능 나이인지 확인하는 메소드"""
return self.__age >= Citizen.drinking_age
def __str__(self):
"""주민 정보를 문자열로 리턴하는 메소드"""
return self.name + "씨는 " + str(self.__age) + "살입니다!"
def get_age(self): # __age에 접근 할 수 있는 메소드
"""숨겨 놓은 인스턴스 변수 __age의 값을 받아오는 메소드"""
return self.__age
def set_age(self, value): # __age에 접근 할 수 있는 메소드
"""숨겨 놓은 인스턴스 변수 __age의 값을 설정하는 메소드"""
self.__age = value
jahyeon = Citizen("구자현", 24, "12345678")
print(jahyeon.get_age())
jahyeon.set_age(25)
print(jahyeon.get_age())
# 24
# 25
➡ 객체의 메소드를 통해 변수 접근하기 #2
- getter 메소드 : 변수의 값을 읽는 메소드
- setter 메소드 : 변수의 값을 설정하는 메소드
getter / setter 메소드를 꼭 만들 필요는 없다
__resident_id
같은 민감한 정보는 값을 읽거나 설정 하면 안됨
👉 캡슐화 정리
- 클래스 밖에서 접근 못하게 할 변수, 메소드 정하기
- 변수나 메소드 이름 앞에 언더바 2개 붙이기
- 변수에 간접 접근할 수 있게 메소드 추가하기 ➡ getter / setter or 다른 용도의 메소드
➡ 객체의 메소드를 통해 변수 접근하기 #3
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.set_age(age) # self.__age = age를 setter 메소드로 변경
self.__resident_id = resident_id
def get_age(self): # __age에 접근 할 수 있는 메소드
"""숨겨 놓은 인스턴스 변수 __age의 값을 받아오는 메소드"""
return self.__age
def set_age(self, value): # __age에 접근 할 수 있는 메소드
"""숨겨 놓은 인스턴스 변수 __age의 값을 설정하는 메소드"""
if value < 0:
print("나이는 0보다 작을 수 없습니다. 기본 값 0으로 나이를 설정하겠습니다.")
self.__age = 0
else:
self.__age = value
jahyeon = Citizen("구자현", 24, "12345678")
print(jahyeon.get_age())
jahyeon.set_age(-10)
print(jahyeon.get_age())
jahyeon.set_age(22)
print(jahyeon.get_age())
jahyeon = Citizen("구자현", -10, "12345678")
print(jahyeon.get_age())
# 24
# 나이는 0보다 작을 수 없습니다. 기본 값 0으로 나이를 설정하겠습니다.
# 0
# 22
# 나이는 0보다 작을 수 없습니다. 기본 값 0으로 나이를 설정하겠습니다.
# 0
➡ decorator를 사용한 캡슐화
property
: 변수의 값을 읽거나 설정하는 구문이 아예 다른 의미로 실행- 캡슐화 전 사용하던 코드를 캡슐화 후 수정할 필요 없음
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.age = age
self.__resident_id = resident_id
@property
def age(self): # _age의 getter 메소드 역할
print("나이를 리턴합니다.")
return self._age
@age.setter
def age(self, value): # _age의 setter 메소드 역할
print("나이를 설정합니다.")
if value < 0:
print("나이는 0보다 작을 수 없습니다. 기본 값 0으로 나이를 설정하겠습니다.")
self._age = 0
else:
self._age = value
jahyeon = Citizen("구자현", 24, "12345678")
print(jahyeon.age) # 같은 이름의 age 메소드를 호출
jahyeon.age = 20 # age.setter 호출, 대입 값을 파라미터로 넘김
print(jahyeon.age)
➡ 객체를 사용할 땐 최대한 메소드로
- 변수 직접 사용을 최소화할수록 유지 보수가 쉬운 코드
- 출생기준 나이가 0살에서 1살로 바뀌면
wjddn.age + 1
로 모두 변경해야 함
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.age = age
self.__resident_id = resident_id
def can_drink(self):
"""음주 가능 나이인지 확인하는 메소드"""
return self.age >= Citizen.drinking_age
# 객체 변수에 직접 접근 했을 때
wjddn = Citizen("박준규", 24, "13579")
# 음주 가능 나이인지 확인
if wjddn.age >= Citizen.drinking_age: # 여기
print("{}님은 음주 가능 나이입니다!".format(wjddn.name))
# 음주 가능 나이인지 확인
if wjddn.age >= Citizen.drinking_age: # 여기
print("들어오세요~!")
# 음주 가능 나이인지 확인
if wjddn.age >= Citizen.drinking_age: # 여기
print("무슨 술을 드시겠습니까?")
- 메소드로 접근했다면 메소드 한개만
self.age + 1
로 바꿔주면 됨
class Citizen:
"""주민 클래스"""
drinking_age = 19 # 음주 가능 나이
def __init__(self, name, age, resident_id):
"""이름, 나이, 주민등록번호"""
self.name = name
self.age = age
self.__resident_id = resident_id
def can_drink(self):
"""음주 가능 나이인지 확인하는 메소드"""
return self.age >= Citizen.drinking_age # 여기
# 메소드로 객체에 접근 했을 때
wjddn = Citizen("박준규", 24, "13579")
# 음주 가능 나이인지 확인
if wjddn.can_drink():
print("{}님은 음주 가능 나이입니다!".format(wjddn.name))
# 음주 가능 나이인지 확인
if wjddn.can_drink():
print("들어오세요~!")
# 음주 가능 나이인지 확인
if wjddn.can_drink():
print("무슨 술을 드시겠습니까?")
Author And Source
이 문제에 관하여(TIL_27 : 추상화, 캡슐화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@wumusill/TIL28-객체-지향-프로그래밍의-4가지-기둥-추상화저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)