Clean Architecture Part 3: Principles
Single Responsibility Principle
하나의 모듈은 하나의, 오직 하나의 Actor 에 대해서만 책임을 져야 한다. The module here refers to a cohesive set of codes.
중복과 병합의 문제
중복
하나의 모듈은 하나의, 오직 하나의 Actor 에 대해서만 책임을 져야 한다. The module here refers to a cohesive set of codes.
예를 들어 급여 애플리케이션의 Employee Class 가 있다고 해보자. SRP 를 위반하는 이 클래스는 다음과 같이 서로 성격이 다른 method 를 가지게 된다.
class Employee():
def __init__(self):
self.employees = []
def getWorkingHours(self):
pass
def calculatePay(self):
"""
finance team needs this api
"""
hrs = self.getWorkingHours()
pay = hrs*10
return pay
def reportHours(self):
"""
HR team needs this api
"""
_hrs = self.getWorkingHours()
hrs = _hrs * 1.3
return hrs
def save(self):
pass
여기서 문제는 만약 getWorkingHours
함수를 바꾸게 되면 서로 다른 actor (finance and HR team) 에게 영향을 준다는 것이다.
이러한 문제는 서로 다른 actor 가 의존하는 코드를 너무 가까이 배치했기 때문에 발생한다. 해결책은 이를 분리하는 것이다.
병합
위 사례에서 finance, HR 각각의 개발자가 Employee 클래스를 수정하게 되면 conflict 가 발생하게 되고 merge 가 불가피하다.
이 역시 서로 다른 액터가 동일한 소스 파일을 접근하려고 하기 때문에 발생하게 된다.
Open-Closed Principle
소프트웨어 개체는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
소프트웨어 개체는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
즉, 아키텍처가 훌륭하다면 변경대신 코드를 추가하는 것만으로 새로운 기능을 추가할 수 있어야 한다는 말이다.
소스 코드 의존성의 조직화
- 화살표의 시작 모듈과 끝 모듈은 시작 모듈이 끝 모듈의 함수(데이터)를 사용하고 있지만, 반대로 끝 모듈은 시작 모듈의 아무것도 사용하지 않음을 의미한다.
- 이중선으로 닫힌 상위 레벨의 컴포넌트는 모두 단방향으로 종속 관계를 이루고 있다. 이들의 화살표는 변경으로부터 보호하려는 컴포넌트를 향하도록 그려진다.
- Interactor 컴포넌트의 경우 다른 모든 것에서 발생한 변경으로부터 보호되는데, 이는 이 컴포넌트가 가장 높은 수준의 정책을 포함하기 때문이다.
- 마찬가지로 상위 레벨의 중요도를 담당할 수록 다른 컴포넌트로 부터 보호된다. 즉, 보호의 계층구조가 수준이라는 개념을 바탕으로 생성된다.
방향성 제어
Financial Data Gateway
인터페이스의 경우 별 역할은 없지만 interactor 와 database 간의 의존성을 역전시키기 위해 존재한다.
이게 없었다면 interactor 는 Financial Data Mapper
에 직접 접근하게 되어 interactor 를 최상위로 보호하고자 하는 목적이 깨지게 된다.
Liskov Substitution Principle
S 타입의 객체 o1 각각에 대응하는 T 타입 객체 o2 가 있고, T 타입을 이용해서 정의한 모든프로그램 P에서 o2의 자리에 o1을 치환하더라도 P의 행위가 변하지 않는다면, S는 T의 하위 타입이다.
정사각형 & 직사각형 문제
S 타입의 객체 o1 각각에 대응하는 T 타입 객체 o2 가 있고, T 타입을 이용해서 정의한 모든프로그램 P에서 o2의 자리에 o1을 치환하더라도 P의 행위가 변하지 않는다면, S는 T의 하위 타입이다.
LSP 를 위반하는 유명한 문제가 있다.
class User():
def __init__(self):
self.rect = Rectangle()
def validate(self):
if self.H == self.W:
raise Exception("It is a square!")
class Rectangle():
def setH():
pass
def setW():
pass
class Square(Rectangle):
def setSide():
pass
위 코드에서 Square
가 Rectangle
을 치환할 수 없다.
Interface Segregation Principle
system S → framework F → database D 의 관계대로 의존도가 성립되어 있을 때, F에서는 불필요한 기능인 S와는 전혀 관계없는 기능이 D에 포함된다고 가정하자. 이 기능 때문에 D 내부가 변경된다면 F를 재배포해야 할 수도 있고, 따라서 S까지 재배포해야 할지 모른다.
이럴땐 operation 단위로 D의 기능을 나누어 F가 각각의 Ops 를 의존하도록 분리할 수 있다.
Dependency Inversion Principle
의존성이 추상 abstraction 에 의존하며 구체 concretion 에는 의존하지 않는 시스템을 말한다.
의존성이 추상 abstraction 에 의존하며 구체 concretion 에는 의존하지 않는 시스템을 말한다.
우리가 의존하지 않도록 최대한 피하고자 하는 것은 변동성이 큰 구체적인 요소다.
안정된 추상화
안정된 소프트웨어 아키텍처란 변동성이 큰 구현체에 의존하는 일은 피하고, 안정된 추상 인터페이스를 선호하는 아키텍처 라는 뜻이다.
- 변동성이 큰 구체 클래스를 참조하지 말 것
- 변동성이 큰 구체 클래스로부터 파생하지 말 것
- 구체 함수를 오버라이드 하지 말 것
DIP 위배를 모두 없앨 수는 없다. 하지만 DIP 를 위배하는 클래스들은 적은 수의 구체 컴포넌트 내부로 모을 수 있고, 이를 통해 시스템의 나머지 부분과는 분리할 수 있다.
Author And Source
이 문제에 관하여(Clean Architecture Part 3: Principles), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@tedlim23/Clean-Architecture-Part-3-Principles저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)