Function & OOP
SUB ROUTINE FLOW
-
FLOW
- 메모리에 적재된 명령어가 순차적으로 ALU에 의해서 실행되는 과정
- 한번 실행이 되면 중간에 멈출 수 없고, 명령어가 다 실행되기전까지 절대 멈출수 없다(Sync / 동기화명령)
-
ROUTINE
- Flow 그 자체를 의미한다
- 메모리에 적재된 명령어를 한번만 실행하는게 아니라 여러번 실행시킬 수 있는것
-
SUB ROUTINE
- 현재 루틴안에서 일어나는게 아니라 밖에서 일어나면 다 서브 루틴(상대적인 관점)
- 메인 플로우에서 서브루틴(하나의 또다른 루틴)를 호출시키면 flow가 이동하여 서브루틴안에 있는 명령어들을 다 실행하고 메인 플로우로 돌아오는게 특징
- 서브루틴을 호출할때는 메인 플로우에서 다시 돌아올 지점을 같이 넣어서 호출한다(돌아올것)
-
MAIN FLOW vs SUB FLOW
- Main FLOW - Main routine이 실행되는 것
- Sub FLOW - Main flow에서 Sub Routine이 실행 되는것
// Main FLOW
const routineA = (b) => {
const result = b * 2
console.log(result)
return result
}
const routineB = (d) => {
const result = d * 3
console.log(result)
return result
}
const b = 10,
d = 30
const a = routomeA(b) // Sub Flow
console.log(a)
const c = routineB(d) // Sub Flow
console.log(c)
Communicate with routine
- MAIN FLOW와 SUB FLOW사이에서의 통신
- 위치 : MAIN FLOW에서 SUB Routine을 호출한 위치(되돌아올 장소)
- 값 : 인자(메인 ⇒ 루틴)와 리턴 (루틴 ⇒ 메인)
const routineA = (arg) => {
const result = arg * 2
return result
}
const b = 10,
c = 20,
d = 30
const b = routineA(a) + routineA(b) + routineA(c)
-
연산자로 인한 스택 메모리 생성
- A만 실행시 문제가 없지만, A의 리턴값과 B의 리턴값을 가지고 연산을 하려고 하면 A 와 연산자를 저장
- B가 실행되고 돌아올때까지 이전 결과값과 연산자를 메모리(스택)를 만들놔야지 연산 진행(아니면 성립불가)
Sub Routine in Sub Routine
-
서브루틴 안에 서브루틴
- 서브루틴 상에서 또다른 서브루틴을 호출하려고 하면 지금 현재의 상태를 Snap Shot을 찍어서 가지고 있고 호출한 루틴이 끝나면 다시 돌아와 그 값을 이용하여 서브루틴을 끝낸다.
-
Keep
- 무리한 call stack은 stackover로 프로그램이 멈춘다.(크롬은 대략 100번 정도)
- 이러한 Keep을 call stack이라고 부른다 ⇒ Stack Memory에 저장
- Snap shot을 찍어서 저장하는 과정을 Keep이라고 부른다
const routineA = (arg) => routineB(arg * 2) // routineB를 호출하기전 routineB에 대한 상태값을 저장해놔야한다.
const routineB = (arg) => arg * 3
const b = 10
const a = routineA(b)
Value vs Reference
-
Primitive Value
- 메모리상에서 값이 복사되는 형태
- 루틴으로 전달될때 값이 복사되서 넘어가기 때문에 의존성이 낮아진다(사이드 이펙트가 없다)
-
Reference Value
- 공유된 객체의 포인터를 전달(전달된 객체를 바꾸면 원래값을 바꾼다??? 값은 원래값 하나뿐)
- 레퍼런스로 넘어온 경우, 포인터를 건드리지 않고 readonly로만 사용하고 만들어서 사용해라(사이드 이펙트)
const r = ({ a, b, ...rest }) => rest
const p = { a: 3, b: 4, c: 5, d: 8 }
const a = r(p)
p !== a
Structured Design
높은 응집도, 낮은 결합도
-
높은 응집도
- 배열을 처리하기 위해서 다른 메소드를 부르는게 아니라 배열의 내장메소드를 이용한다
- Math 내장객체는 높은 응징도(수학에 관련된 모든 함수)
- 어떤 함수를 처리하기 위해 여러 함수를 호출하는게 아니라 하나의 함수만으로 처리한다
-
낮은 결합도
- 어떤 함수를 사용하려면 앞에 40개의 함수를 가지고 있어야 해 높은 결합도
- Math 함수 ⇒ floor round (필요할때 가져다가 쓴다 = 의존성이 제로)
COUPRING
-
정의
- 의존성보다는 더 포괄적인 의미를 가지고 있다.
- 의존성은 방향을 가지고 잇다. 커플링은 둘이 의존을 양쪽에서 하고 있다.
-
CONTENT - 강결합
- A클래스 속성 v가 변경되면 즉시 B클래스 깨짐
- B클래스에서 A클래스의 속성을 다 볼수 있기 때문에
- 이렇게 대부분이 코딩을 하고 있으며, 하면 안된다.
const A = class {
constructor(v) {
this.v = v
}
}
const B = class {
constructor(a) {
this.v = a.v
}
}
const b = new B(new A(3))
-
COMMON - 강결합
- COMMON이란 전역객체, 공유객체, 인메모리에 안에 있는 객체를 의미한다.
- 1:1 구조를 COMMON으로 바꾼것일 뿐
const Common = class {
constructor(v) {
this.v = v
}
}
const A = class {
constructor(c) {
this.v = c.v
}
}
const B = class {
constructor(c) {
this.v = c.v
}
}
const a = new A(new Common(3))
const b = new B(new Common(3))
-
External 강결합
- 이 결합은 피할수 없다 정복해야 한다
- member.json에 의존적이라서 어쩔수 없다
- swagger 같은 것이 있어서 키랑 응답값에 대해서 정의
- 키가 없거나 다를 경우를 대비해서 throw 처리를 해줘야 한다.
const A = class{
constructor(member){
this.v = member.name;
}
};
const B = class{
constructor(member){
this.v = memver.age
}
fetch('/member')
.then(res => res.json())
.then(member => {
const a = new A(member);
const b = new B(member);
})
-
Control 강결합
- A 클래스 내부의 변화는 B클래스의 오작동을 유발
- flag를 기준으로 proces를 처리하기 때문에 문제가 많다.
- 팩토리 패턴에서 자주 발생되는 문제이며, 전략 패턴으로 해결해야한다.
const A = class {
process(flag, v) {
switch (flag) {
case 1:
return this.run1(v)
case 2:
return this.run2(v)
case 3:
return this.run3(v)
}
}
}
const B = class {
constructor(a) {
this.a = a
}
noop() {
this.a.process(1)
}
echo(data) {
this.a.process(2, data)
}
}
const b = new B(new A())
b.noop()
b.echo('test')
-
STAMP - 강결합 or 유사약결합
- A와 B는 ref로 통신함 ref에 의한 모든 문제가 발생할 수 있음
- A클래스는 단순히 add라는 행위에 초점을 둬야 하는데 객체로 받아오면 객체의 key가 변경이되면 오류 발생
- B클래스에서는 count 그 값자체만 주고 update는 본인이 해야함
const A = class {
add(data) {
data.count++
}
}
const B = class {
constuctor(counter) {
this.counter = counter
this.data = { a: 1, count: 0 }
}
count() {
this.counter.add(this.data)
}
}
const b = new B(new A())
b.count()
b.count()
-
DATA - 약결합
- A와 B는 value로 통신함 모든 결합문제에서는 자유로워짐
- 하지만 A클래스에서 당연히 값이 온다고 가정했기 떄문에 데이터에 대한 커플링 발생
- 루틴간 통신하려고 할때는 값으로!!
const A = class {
add(count) {
return count++
}
}
const B = class {
constuctor(counter) {
this.counter = counter
this.data = { a: 1, count: 0 }
}
count() {
this.data.counter = this.counter.add(this.data.count)
}
}
const b = new B(new A())
b.count()
b.count()
COHESION
-
COINCIDENTAL(우연히)
- Util 같이 메소들이 아무런 관계가 없음. 다양한 이유로 수정됨
const Util = class { static isConnect() {} static log() {} static isLogin() {} }
-
LOGICAL
- 사람이 인지할 수 있는 논리적 집합. 언제나 일부만 사용됨.
- 동등한 지식을 가진 사람이 인식할 수 있는 수준의 응집된 함수의 모임
- 도메인이 일반적이고 누구든지 알수있다면 진행(Math, URL)
const Math = class { static sin(r) {} static cos(r) {} static random() {} static sqrt(v) {} }
-
TEMPOLAL
- 시점을 기준으로 관계없는 로직을 묶음.
- 관계가 아니라 코드의 순서가 실행을 결정
- 역할에 맞는 함수에게 위임해야 함.
const App = class { init() { this.db.init() this.net.init() this.asset.init() this.ui.start() } }
-
PROCEDURAL
- 외부에 반복되는 흐름을 대체하는 경우
- 순서정책변화에 대응불가
const Account = class { login() { p = this.ptocken() s = this.stoken(p) if (!s) this.newLogin() else this.auth(s) } }
-
COMMUNICATIONAL
- 하나의 구조에 대해 다양한 작업이 모여있음.
- 배열처럼 push/pop/slice 하나의 목적
- 하나의 객체가 하나의 역할을 수행하는것이 좋다.
const Array = class { push(v) {} pop() {} shift() {} unshift() {} }
-
SEQUENTIAL
- 실행순서가 밀접하게 관계되며 같은 자료를 공유하거나 출력결과가 연계됨
- 프로시져와 커뮤니케이션의 복합체, chainning 함수
const Account = class { ptocken() { return this.pk || (this.pk = IO.cookie.get('ptocken')) } stoken() { if (this.sk) return this.sk if (this.pk) { const sk = Net.getSessionFromProken(this.pk) sk.then((v) => this.sk) } } auth() { if (this.isLogin) return Net.auth(this.sk).then((v) => this.isLogin) } }
-
FUNCTIONAL
- 역할모델에 충실하게 단일한 기능이 의존성 없이 생성된 경우
결합도와 응집도는 서로가 다른 방향으로 향하게 되므로 둘 사이의 밸런스를 찾는 것이다.
추가
-
순수한 함수를 만들때 this가 필요해?
- 함수는 에로우로 만들자
- 메소드는 클래스 문법으로 만들자
-
자바스크립트는 리턴값을 정의하지 않으면 무조건 undefined가 된다
-
undefined의 두가지
- new Array(10) ⇒ 길이가 10인 undefiend
- var a = undefined ⇒ 값이 undefiend
-
es6부터는 객체의 키값이 선언한 순서대로 순서를 보장한다