일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- clean architecture
- 리펙토링
- Protocol
- SWIFT
- map
- UICollectionView
- combine
- ribs
- Clean Code
- swiftUI
- rxswift
- 클린 코드
- MVVM
- uiscrollview
- 스위프트
- Observable
- ios
- Refactoring
- UITextView
- collectionview
- tableView
- 리팩토링
- 애니메이션
- 리펙터링
- swift documentation
- uitableview
- Human interface guide
- Xcode
- HIG
- RxCocoa
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[Clean Architecture] 5. 객체 지향 프로그래밍 (Object-Oriented Programming), 의존성 역전 본문
[Clean Architecture] 5. 객체 지향 프로그래밍 (Object-Oriented Programming), 의존성 역전
jake-kim 2021. 3. 12. 02:31객체 지향 프로그래밍
- 3가지 요소를 반드시 지원하는 프로그래밍 방법
- 캡슐화(encapsulation), 상속(inheritance), 다형성(polymorphism), 다형성을 이용한 의존성 역전(dependency inversion)
- 사실생 캡슐화, 상속은 객체 지향 언어가 탄생하기 전부터 C에서도 존재
- 객체 지향 프로그래밍이란 다형성을 이용하여 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있는 능력
(모듈의 독립성, 배포 독립성)
캡슐화 (= 은닉화)
- 데이터와 함수가 응집력 있게 구성된 집단을 서로 구분 짓는 선을 그을 수 있게끔 하는 개념
- 사용하는 쪽에서 멤버 변수들의 정보를 모르고 있어야 완전한 캡슐화
- private, public으로 표현: 사용쪽에서 특정 구조체의 멤버에는 접근하지 못하지만 함수를 호출할 수 있는 형태
- 만약 결합도가 커지는 상황이 된다면 (A가 B, C를 사용할 때, 만약 B와 C도 서로 참조하고 있다면 B나 C 중 하나만 걷어내고 싶어도 걷어내기가 힘듦 -> 교체하기 힘듦 -> 유지보수가 힘듦)
ex) c언어 에서의 완벽한 캡슐화(은닉화): point.h를 사용하는 측에서 strcut Point의 멤버에 접근할 수 없지만 makePoint(), distance()함수를 호출만 가능 (Point구조체의 데이터 구조와 함수가 어떻게 구현되어 있는지 모르는 상태)
// point.h
struct point;
struct point* makePoint(double x, double y);
double distance (struct Point* p1, strcut Point* p2);
// point.c
#include "point.h"
#include <stdlib.h>
#include <math.h>
struct Point {
double x,y;
}
struct Point* makePoint(double x, double y) {
struct Point* p = malloc(sizeof(struct Point));
p->x = x;
p->y = y;
return p;
}
double distance(struct Point* p1, struct Point* p2) {
double dx = p1->x - p2->x;
double dy = p1->y - p2->y;
return sqrt(dx*dx + dy*dy);
}
- 객체 지향 언어가 완전한 캡슐화가 아닌 이유: 사용하는 쪽에서 멤버 변수들의 정보를 모르고 있어야 완전한 캡슐화이므로 아래는 완전한 캡슐화가 불가 -> 사용하는 쪽에서 x, y멤버에 접근할 수 있지만 사용자는 멤버변수가 존재한다는 것을 알고 있으므로
// 객체지향 언어 C++ 예시
// point.h
class Point {
public:
Point(double x, double y);
double distance(const Point& p) const;
private:
double x;
double y;
};
상속
- 어떤 변수와 함수를 하나의 유효 범위로 묶어서 재정의하는 것
- C언어에서도 상속을 구현할 수 있지만, 객체 지향 언어에서 조금 더 편리한 방식을 제공하는 것
ex) C언어에서 상속 구현: main에서 NamedPoint구조체가 마치 Point로 부터 파생된 형태 (두 변수 x, y의 순서가 Point와 동일하기 때문)
// namedPoint.h
struct NamedPoint;
struct NamedPoint* makeNamedPoint(double x, double y, char* name);
void setName(struct NamedPoint* np, char* name);
char* getName(struct NamedPoint* np);
// namedPoint.c
#include "namedPoint.h"
#include <stdlib.h>
struct NamedPoint {
double x,y;
char* name;
};
struct NamedPoint* makeNamedPoint(double x, double y, char* name) {
struct NamedPoint* p = malloc(sizeof(struct NamedPoint));
p->x = x;
p->y = y;
p->name = name;
return p;
}
void setName ...
char* getName ...
// main.c
#include "point.h"
#include "namedPoint.h"
#include <stdio.h>
int main(int ac, char** av) {
struct NamedPoint* origin = makeNamedPoint(0.0, 0.0, "origin");
struct NamedPoint* upperRight = makeNamedPoint(1.0, 1.0, "upperRight");
printf("distance = %f\n", distance((struct Point*) origin, (struct Point*) upperRight));
}
다형성
- 아이디어는 프로그램의 '장치 독립적' (어떤 기기에서도 실행)을 위함
- 같은 자료형에 여러 가지 객체를 대입하여 다양한 결과를 얻어내는 성질
- 주의 - 상속이 아닌 다형성의 키워드: Overriding, Overloading, casting
다형성이 중요한 이유 - 의존성 역전
- 객체 지향의 꽃은 '의존성 역전' 개념
- 객체 지향은 소스코드의 의존성을 어디에서든 역전시킬 수 있는 것
- 아이디어는 ''배포 독립성(independent deployability)': 특정 컴포넌트의 소스 코드가 변경되면, 해당 코드가 포함된 컴포넌트만 다시 배포
- 모듈을 독립적으로 배포할 수 있게 되면 서로 다른 팀에서 각 모듈을 독립적으로 개발하는 것이 가능
* 의존성 역전이 없는 경우: 비즈니스 로직은 UI와 DataBase에 의존하고 있기 때문에, UI나 DataBase가 바뀌면 BusinessLogic을 변경해주어야 하며, UI와 DB둘 중 하나만 바뀌어도 영향이 가는 구조
* 의존성 역전이 된 경우: BusinessLogic에서의 소스코드에서는 UI나 DB를 호출하지 않으며 UI나 DB와는 무관하게 BusinessLogic을 독립적으로 배포 가능
- 객체 지향 언어에서만 의존성 역전이 가능한 이유?:
* 객체 지향 언어가 아닌 경우, 의존성 역전이 불가능: 제어흐름(의존성, 함수 호출 순서)가 모듈의 단계별로 필요한 수준에 맞게 일방적으로만 가능
* 객체 지향 언어인 경우:
HL1모듈은 I인터페이스를 참조하고 있지만 오버라이딩 된 ML1의 F()사용
-> 객체 지향이 아니라면 HL1은 I를 가리키고, I는 ML1을 가리키는 형태이지만 overriding에 의하여 의존성 역전
-> 소스 코드의 의존성(상속관계)가 제어 흐름(함수 호출)과는 반대
-> 만약 I도 ML1에 의존하고 있으면, ML1이 바뀔 때 I도 같이 바꾸어야 하는 상황 존재(아래 상황은 ML1에 수정사항이 생기면 ML2, ML3로 구현하여 기존 코드에 주입해서만 사용하면 해결)
swift에서의 의존성 역전 적용 방법
- Interface개념을 가지고 있는 protocol 사용
- 사용하는 HL1모듈에서는 protocol의 함수를 호출 -> protocol을 구현한 ML1 모듈이 호출 -> ML1이 바뀌어도 쉽게 다른 모듈로 갈아치우기 쉬움
- 테스트 코드에 이점: protocol을 참조하고 있는 HL1 입장에서 F()에 대한 테스트 코드를 만들고 싶을 때, ML1_test로 새로 만들어 해당 구현체만 바꾸어서 주입해주면 테스트가 용이한 이점 존재
- 주의할 점은 HL1입장에서 protocol로 구현된 모듈을 참조하고 있어야하며, 그 모듈의 구현체는 바꾸기 쉽도록 '주입'하는 형태가 되어야 함
* 출처: Clean Architecture
'Clean Architecture > Clean Architecture 기초' 카테고리의 다른 글
[Clean Architecture] 7. 설계원칙 (SOLID) (0) | 2021.06.17 |
---|---|
[Clean Architecture] 6. 함수형 프로그래밍 (0) | 2021.03.24 |
[Clean Architecture] 4. 구조적 프로그래밍 (structured programming), 테스트 코드가 중요한 이유 (0) | 2021.03.06 |
[Clean Architecture] 3. 패러다임 개요 (구조적, 객체지향, 함수형) (0) | 2021.03.05 |
[Clean Architecture] 2. 두 가지 가치(behavior, architecture) (0) | 2021.02.27 |