- 멘토링 회고 : package 구조
- 도메인 주도 개발 시작하기
- 코테 구현 한문제
어제도 어찌저찌 나름대로 잘 방어한 멘토링 시간이었다 ^^.(멘토님 입장에선 아닐 확률 94.125%) SOLID에 대해서 얘기 나눴는데, 기억 나는것 + 몰랐던 것만 간단히 정리!
SRP는 "responsibility"를 나누는 과정이다. 클래스마다 하는 일이 나눠져있으면 하나의 일을 하는 코드가 한 클래스에 몰려있게되고, 응집력이 높아셔 특정 기능의 변경이 다른 기능에까지 영향을 끼치는 것을 막을 수 있다.
추가로 재사용성을 높일 수 있는데, 이는 한 코드에 기능이 모여있으니 특정 기능을 사용할 때 가볍고 편하게 해당 모듈을 불러올 수 있기 때문이다. SRP를 위반한 클래스 코드는 내부에 여러 기능이 복잡하게 얽혀있기 때문에, 특정 기능을 사용하고 싶을 때 클래스 내의 부분을 떼어내 사용하기 힘들다.
하위 클래스에서 사전 조건이 강해져선 안되며, 사후 조건이 약해져서도 안된다. 클라이언트 코드가 상위 클래스의 메소드를 수행했을 때 기대하는 모든 것을 하위 클래스에서 수행할 수 있어야 진정한 다형성을 구현했다고 할 수 있기 때문이다.
멘토님께서 이에 대한 예시를 들어보라 했을 때 어벙~ 했는데 좋은 예시를 하나 알아갔다. 전체 정수값을 인자로서 받는 상위 클래스 메소드를 오버라이딩한 하위 클래스 메소드가 인자로 양수만을 허용하는 예시다. 사전 조건이 정수에서 양수로 강해졌다. 이는 LSP 위반이다!
DIP는 고수준 컴포넌트가 저수준 컴포넌트에 의존하면 안된다는 원칙이다. 자주 바뀌는 구체적 기술인 저수준 컴포넌트에 의해 고수준 컴포넌트가 영향을 쉽게 받으면 변경에 취약한 코드가 되기 때문이다. 그럼 여기서 고수준 컴포넌트란 정확히 뭘까?! 사실 지금도 몰라~~!
일단 상대적으로 접근하는게 베스트긴 한데, 고수준 컴포넌트는 정책적인 것이라고 한다? 도메인에 대한 제약사항 말이다. 뭐 배송 상태를 바꿀 수 있는 것은 상품 출고 이전이라든가, 상품 가격으로 음수가 오면 안된다든가.. 슬랙을 예시로 들면 슬랙 메세지 속 스레드는 반응이 추가되면 삭제할 수 없다든가.. 그런 데이터 제약사항을 표현해 놓은 것을 일반적으로 "고수준 컴포넌트"로 정의하는듯하다.
DIP를 위반하면? 기술이 바뀌었다고 이런 도메인 정책까지 바뀌어야 하는 상황이 올 수도 있다. 기술에 따라 달라지는 정책이라니 뭔가 힙하다 ㅋㅋ 아무튼 이를 피하기위해 DIP에서는 OCP와 같은 방법으로, 고수준 컴포넌트가 의존하면서 저수준 컴포넌트가 구현할 수 있는 상위 인터페이스를 하나 둔다.
오랜만의 독서!!! 는 아니고, (그 전에 오브젝트와 HTTP 완벽 가이드를 주구장창 읽었기 때문) 진짜 코드와 관련된 독서라 신난다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
verify
메소드들을 구성한다.Money
클래스 타입, ID를 표시하기 위한 UserId
클래스 타입을 만드는 식으로 "의미를 명확하기 위해" Value 타입을 사용하기도 한다. 저장값의 의미를 명확하게 하기 위함이다.null
X)를 만드는데 방해가 된다.Money
, Address
등. 밸류는 불변일 것을 권장(정책 로직을 수행시키기 위함)@Transactional
, @Entity
, @Table
등)단순 구현 문제! 근데 itertools
의 중복순열을 이용한.
from itertools import product
def solution(users, emoticons):
n,m = len(users),len(emoticons)
ans_reg,ans_amount = -1,-1
sales = product((10,20,30,40),repeat=m)
for sale in sales:
cur_reg = 0 # 지금 이 할인율에서의 가입자
user_purchase_amount = [0 for _ in range(n)] # 각 유저의 구매 금액
for i in range(m):
price = emoticons[i] * (1-(sale[i]*0.01))
for j in range(n):
if (users[j][0]<=sale[i]):
user_purchase_amount[j] += price
for i in range(n):
if (user_purchase_amount[i] >= users[i][1]):
user_purchase_amount[i] = 0
cur_reg += 1
if (cur_reg<ans_reg):
continue
elif (cur_reg>ans_reg):
ans_reg = cur_reg
ans_amount = sum(user_purchase_amount)
else:
ans_amount = max(ans_amount,sum(user_purchase_amount))
return [ans_reg,int(ans_amount)]
할인율의 경우가 4개, 이모티콘이 최대 7개라는거 보고 딱 브루트포스 구현 문제인 것에 감이 왔다. 4개의 할인율 각각에 대해 모든 이모티콘의 값을 중복순열로 구했다. 존재하는 모든 경우를 체크한거지? 그때마다 사용자들의 구매 여부 및 이모티콘 플러스 가입 여부를 체크했다. user_purchase_amount
의 각 인덱스에는 사용자들의 구매 금액이 담겨있다. 특정 유저가 이모티콘 플러스에 가입할 경우 해당 인덱스 값은 0, cur_reg
값을 더해 가입자 수가 늘어났음을 표시했다!
나머지는 간단. 가입자수가 현재 최선 이상일 경우 이모티콘 판매 액수인 ans_amount
를 업데이트한다. 모든 경우를 돌아본 뒤 나온 최선의 답을 리턴!
https://armadillo-dev.github.io/design/programming/desing-by-contract/