클린 아키텍처란? SOLID 원칙
솔리드 원칙
단일책임원칙
-각 소프트웨어 모듈은 변경의 이유가 단 하나여야만 한다(엉클밥이 말함)
-최근에 출간된 클린 아키텍쳐에서는 하나의 모듈은 오직 하나의 액터에서만 책임을 져야 한다라고 말하고 있음
*유의할 점: 하나의 클래스가 하나의 일을 한다는 뜻이 아님!
액터라고 하면 유스케이스 다이어그램을 보면 설계를 시작하기 전에 어떤 유스케이스들이 있고, 그 유스케이스들을 처리하고 유스케이스들의 요청을 주는 어떤 사람 내지는 사물들을 뜻함
하나의 모듈에 어떤 액터가 연루되어 있는지가 중요하다
이 클래스는 왜 잘못된 설계일까?
이미지 클래스가 하나 있고
그 안에 프라이빗으로 된 버퍼 데이터가 하나 있음
그리고 세개의 퍼블릭 메소드를 가지고 있다
정답: 세 개의 메소드가 서로 매우 다른 세 개의 액터를 책임지고 있기 때문
loadFromServer < Downloder
drawToScreen < View
cropByShape < Transfomer
이미지는 이미지에 대한 정보만을 가지고 있어야 함(ex-이미지 사이즈)
이 클래스는 왜 잘못된 설계일까?
실제로는 또 다른 액터가 더 있다면? 로컬 캐시 저장소
로드할 때는 캐시에 저장될 테고
드로우 할 때는 읽어드릴테고
크랍했을 때는 두 가지가 다 일어남
해결책: 세 개의 클래스로 분리하기
퍼사드 패턴 이용
사용자 입장에서 세개의 클래스를 일일이 생성하는 것은 사용성이 떨어지므로
개방-폐쇄 원칙
소프트웨어 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
(열려있다는 것은 데이터 구조의 필드를 추가하거나 함수에 새로운 요소를 추가하는 것이 가능해야한다)
데이터 구조 확장이란 함수 내부에 멤버 변수들이 늘어 날 수 있다
멤버 변수들이 늘어난다고 해서 이 클래스나 모듈을 사용하는 사용자 입장에서 뭔가 변경이 있지 않게 해야 함
그래서 웬만한 데이터 구조들은 프라이빗이어야함
함수에 새로운 요소를 추가해야 하는 경우도 있음
기능을 확장하는 경우는 새로운 메소드를 추가하거나
이거를 확장하는 다른 인터페이스를 통해서 기능이 더 늘어날 수 있도록 하는 게 더 좋은 원칙이라고 할 수 있다
닫혀있다라고 하는 거는 내부 코드를 변경한다고 해도 이거를 사용하는 외부모델에 영향을 미치면 안 됨
프리젠터 뷰
프리젠터는 상위 개념
프리젠터는 뷰에 의존하지 않지만 뷰는 프리젠터에 의존함
예시) file.open()
파일 오픈하는 방식이 전면적으로 바뀌었다고 치면
그것들이 전부 내부에 있다 하면
리스코프 치환 원칙
대전제
SOLID는 기본적으로 중간 규모(모듈)에 대한 원칙임을 기억하면 이해하기가 더 쉬움
*여기에서 말하는 중간 규모란 ? 클래스들이 모여있는 모듈 구조
목표: 중간 규모의 소프트웨어 구조가 아래와 같은 요구를 만족하도록..
유지보수성: 변경에 유연해야 한다.
가독성: 이해하기 쉬워야 한다. (ex- 모듈이 나눠진 구조나, 클래스의 이름이라든가 배치들만 봐도 이해하기 편해야 하고 이클래스는 무슨 일을 하는 클래스인)
낮은 결합도
높은 응집도란?
하나의 모듈안에서는 높은 응집도를 가지고 있어야 함
그 안에서 필요한 구현들, 그 중요한 책임들은 그 안에서 해결이 되어야 한다.
낮은 결합도란?
그 클래스에서 하고 있는 중요한 일들을 위해서 다른 클래스에 일들이 필요하다고 하면 그건 높은 결합도를 갖고 있는 거죠
리스코프 치환 원칙의 또다른 교훈
왜 상속보다 조합인가?
상속으로 인한 부작용
자식은 부모가 갖고 있는 행위를 정확히 이해하고
이 행위가 깨지지 않도록 할 의무가 있음
인터페이스 분리 원칙
각 소프트웨어 모듈은 자신이 사용하지 않는 것에 의존하지 않아야 한다
ops 제공하는 클래스 세가지 유저들이 사용
각 유저들이 하나의 클래스만 사용하고 있음
그러면 그 원칙에 위배도미
외부 모듈에 필요한 인터페이스만 노출한다
왜 인터페이스가 필요한가?
추이 종속성을 막기 위해서(추이 종속성이란
A
↓
B
↓
C
이런식으로 A클래스가 C까지 종속받아 의존성이 생김
의존성 역전 원칙
모듈간에 적용되는 원칙이라고 볼 수 있음
높은 수준의 코드는 낮은 수준의 세부사항 구현에 절대로 의존해서는 안 되며, 그 반대여야 한다.
======================================================================================
2회차 들으면서 다시 필기한 것들
SOLID는 중간 규모에 대한 원칙이라고 생각하면 쉬울 것 같음
중간규모란? 클래스들이 모여있는 모듈 구조
단일 책임의 원칙
각 소프트웨어 모듈은 변경의 이유가 단 하나여야 한다
(하나의 모듈은 오직 하나의 액터에 대해서만 책임져야 한다)
*하나의 클래스가 하나의 일을 해야 한다는 건 아님
단일 책임의 원칙을 지키기 위해 클래스가 여러개로 나뉠 수도 있는데 이 경우 사용성이 떨어질 수도 있다
이를 보완하는 방법으로 퍼사드 패턴을 사용하기도 함.
퍼사드 패턴이란?
유틸리티 내지는 래퍼들을 통해서 감춰져있는,아니면 여러군데 분산되어 있는 것을 일관되게 제공할 수 있는 어떤 인터페이스를 제공하는 것
개방 폐쇄 원칙 (OCP)
소프트웨어 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다
예를 들어 파일 IO 라이브러리가 있다고 했을 때 여기에서 변경이 일어나거나 버전업이 될 때 사용자가 계속 바꿔줘야 함 <-이런부분은 최대한 감추고 의미있는 정보만 보여주자
"열려 있다"는 것은?
데이터 구조에 필드를 추가하거나, 함수에 새로운 요소를 추가하는 것이 가능해야 함
데이터 구조 확장 예) 함수 내부에 있는 멤버변수가 늘어날 수 있음
이 클래스 또는 모듈을 사용하는 사용자 입장에서 뭔가 변경이 있거나 하지 않도록 해야함(그래서 웬만한 데이터 구조들은 private어야함)
새로운 메서드를 추가하거나, 인터페이스를 통해서 기능이 더 늘어날 수 있도록 하는 게 좋은 방향이라고 할 수 있음
"닫혀 있다"는 것은?
내부 코드를 변경해도 이를 사용하는 외부 모듈에는 영향을 미치지 않아야 한다
예시) 파일 오픈하는 방식이 전면적으로 바뀌었다고 치면 그런데 그것들이 전부 내부에 있다고 하면 사용하는 측면에서는 전혀 수정할 필요가 없음
클래스 간에서도 개방-폐쇄의 원칙 중요함
why? 하나의 모듈안에서도 여러개의 클래스가 상호작용을 하기 때문
그안에서도 의존 관계가 생김
이런 의존 관계 안에서도 계층 구조가 같이 있어서 상위레벨이 있다고 하면 하위 계층에 의존하지 않도록 해야함(하위계층은 상위계층을 의존할 수 있지만 반대는 성립하지 않도록 만들어야 함)
리스코프 치환 원칙(절대적인 건 아님)
대체 가능한 컴포넌트들을 이용해 시스템을 만들 수 있으려면, 이들의 서브타입ㅇ들은 반드시 서로 치환 가능해야 한다
상속보다는 조합
예시)상속으로 처리하는 것보다 안에 멤버변수를 추가해서 처리하는 방법
인터페이스 분리 원칙
각 소프트웨어 모듈은 자신이 사용하지 않는 것에 의존하지 않아야 한다
의존성 역전 원칙
높은 수준의 코드는 낮은 수준의 세부사항 구현에 절대로 의존해서는 안 되며, 그 반대여야 한다.
본 글은 The RED : 강사룡의 앱 안정성 및 확장성 강화를 위한 Android 아키텍처 강의를 듣고 강의 내용을 요약해서 정리한 글입니다.