프로그램을 설계할 때 발생하는 문제점들을 해결할 수 있도록 설정한 '규약'
디자인 패턴을 기반으로 만들어지는 것으로 라이브러리와 프레임워크가 있습니다.
Q1: 라이브러리와 프레임워크의 차이점이 뭔가요?
A1: 규칙을 중심으로 답하기!
라이브러리는 규칙이 존재하지 않아 개발자가 필요한 기능을 자유롭게 선택할 수 있습니다. 반면, 프레임워크는 규칙이 존재하여 보다 제한적입니다. 하지만 제공된 규칙과 구조를 통해 개발 시간을 단축하고 일관성을 유지할 수 있습니다.
하나의 클래스에 하나의 인스턴스만 가지는 패턴
하나의 인스턴스를 다른 모듈들이 공유하기 때문에 인스턴스 생성 비용 감소
→ 그래서 인스턴스 생성에 비용이 많이드는 I/O 바운드 작업에 싱글톤 사용
⚡ I/O 바운드
하나의 클래스에 하나의 인스턴스를 만들어 이 인스턴스를 여러 모듈들이 공유하는 형태
방법
인스턴스 선언 후 없으면 생성
문제점
멀티스레드라는 자바의 특성상 이 방법을 사용하면 원자성이 결여됨
👉 인스턴스 2개 생성 가능 👉 싱글톤 개념 불만족
방법
인스턴스 반환 전까지 다른 스레드가 접근할 수 없도록 lock을 걸어줌
문제점
호출할때마다 걸리는 lock이 성능 저하를 유발
방법
매번 lock을 걸지 않고 인스턴스가 null일때만 lock을 걸어줌으로서 성능 저하 해결
참고) volatile
서로 다른 캐시메모리가 아닌 메인메모리에서 변수를 가져오게 만듦
👩💻코드: private volatile Singleton instane
방법
인스턴스 선언과 동시에 생성
문제점
멀티스레드로 인해 발생하는 문제점은 해결가능하지만, 자원 낭비가 발생
방법
위 방법과 유사하나, 생성시 블록을 사용
⚡ lazy instantiation이라고도 부름
방법
필요할 때 만 정적 멤버를 선언하여 불필요한 자원할당 방지
코드
class Singleton:
__instance = None # 인스턴스를 저장할 클래스 변수
@staticmethod
def getInstance():
if Singleton.__instance is None: # 인스턴스가 없는 경우
Singleton() # 인스턴스 생성
return Singleton.__instance # 인스턴스 반환
def __init__(self):
if Singleton.__instance is not None: # 이미 인스턴스가 생성된 경우
raise Exception("이미 인스턴스가 생성되었습니다.")
else:
Singleton.__instance = self # 클래스 변수에 인스턴스 저장
# 사용 예시
singleton_instance1 = Singleton.getInstance() # 인스턴스 생성
singleton_instance2 = Singleton.getInstance() # 이미 생성된 인스턴스 반환
print(singleton_instance1 is singleton_instance2) # True (두 인스턴스가 동일한지 비교)
방법
스레드세이프한 점을 보장하며 인스턴스 생성
코드
from enum import Enum, auto
class Singleton(Enum):
INSTANCE = auto() # enum 멤버로 인스턴스 생성
def __init__(self):
self.some_property = "example" # 추가적인 속성 정의
# 사용 예시
singleton_instance1 = Singleton.INSTANCE
singleton_instance2 = Singleton.INSTANCE
print(singleton_instance1 is singleton_instance2) # True (두 인스턴스가 동일한지 비교)
print(singleton_instance1.some_property) # example (추가적인 속성 접근)
객체를 사용하는 코드에서 생성 부분을 떼어내 추상화한 패턴
💡 주요사항
떼어냄, 즉 클래스 분리를 위해 느슨한 결합으로 구성됨
각기 다른 자료구조를 한 개의 인터페이스로 순회 가능
# 문자열로만 이뤄진 컬렉션의 이터레이터 구현
class StringIterator:
def __init__(self, collection):
self.collection = collection
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.collection):
raise StopIteration
value = self.collection[self.index]
self.index += 1
return value
# 숫자로만 이뤄진 컬렉션의 이터레이터 구현
class NumberIterator:
def __init__(self, collection):
self.collection = collection
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.collection):
raise StopIteration
value = self.collection[self.index]
self.index += 1
return value
# 문자열 컬렉션 생성
string_collection = ["apple", "banana", "cherry"]
# 숫자 컬렉션 생성
number_collection = [1, 2, 3, 4, 5]
# 문자열 컬렉션 순회
string_iter = StringIterator(string_collection)
for item in string_iter:
print(item)
# 숫자 컬렉션 순회
number_iter = NumberIterator(number_collection)
for item in number_iter:
print(item)
의존성의 높고 낮음은 좋고 나쁨으로 정의할 순 없다.
하지만 일반적으로 과도한 의존성은 코드의 유지보수성과 확장성을 저해한다.
지금 공부하는 CS는 면접 목적도 있기 때문에!
면접관님께서
👩💻 의존성이 너무 높을 경우 어떻게 해야하는가?
를 질문하신다면
을 대답할 수 있다.
메인 모듈이 ❌직접❌ 하위 모듈에 연결되어 의존성을 주는 것이 아니라, 중간에 의존성 주입자를 넣어 메인모듈이 ⭕간접적⭕으로 의존성을 주입하는 것이다.
의존성 주입을 할 때 적용되는 법칙입니다.
💡 참고
추상화(Abstraction)
객체들 간의 공통된 특성과 동작을 추상적, 독립적으로 정의
세부사항(Details)
코드의 특정 부분이나 구현 세부 사항을 나타내며, 추상화와는 반대
👉 세부사항은 구현의 변화나 변경에 따라 달라짐
프로그램을 설계할 때 발생하는 문제점들을 해결할 수 있도록 설정한 '규약'
개념: 하나의 클래스에 하나의 인스턴스만 가지는 패턴
필요성: 객체의 유일성을 보장해주어야하는 경우, 인스턴스 생성 비용이 큰 경우
사용사례: 데이터베이스 연결, 네트워크 연결
효과적인 구현 방법: lazy instantiation, enum