좋은 프로그램이란, 좋은 함수란, 좋은 서브루틴이란 높은 응집도와 낮은 결합도를 가질 수 있도록 작성해야한다.
높은 응집도와 낮은 결합도를 가지는 코드는 유지보수에 용이하다.
예를 들어, Stack을 제어하는 push, pop이라는 메서드가 존재한다고 가정했을 때, push는 A라는 클래스에 존재하고, pop은 B라는 클래스에 존재하게 된다면, 일관성을 느낄 수 없고 메서드 사용 시에 어느 메서드가 어느 클래스에 존재하는 지 외워서 사용할 수 밖에 없다.
또한 높은 결합도를 가지고 있는 코드를 생각해보면, 하나의 메서드를 호출하는데 필요한 객체가 무수히 많아진다. 이렇게 되면 클래스 하나의 수정이 필요할 때, 해당 클래스에 종속되어 있는 클래스들에 문제가 생기지 않는 지 모두 고려하여 수정해야하는 문제가 생기고, 이는 곧 유지보수의 어려움으로 이어진다.
결합도와 응집도를 단계별로 나누어진 모델이 존재하는데, 아래와 같다. 오른쪽에 있을수록 바람직하다.
Content > Common > External > Control > Stamp > Data
class A {
constructor(v) {
this.v = v;
}
};
class B {
constructor(a) {
this.v = a.v;
}
};
const b = new B(new A(3));
class Common {
constructor(v) {
this.v = v;
}
};
class A {
constructor(c) {
this.v = c.v;
}
};
class B {
constructor(c) {
this.v = c.v;
}
};
const a = new A(new Common(3));
const b = new B(new Common(3));
class A {
constructor(member) {
this.v = member.name;
}
};
class B {
constructor(member) {
this.v = member.age;
}
};
fetch('/member').then(res => res.json()).then(member => {
const a = new A(member);
const b = new B(member);
});
class A {
process(flag, v) {
switch(flag) {
case 1: return this.run1(v);
case 2: return this.run2(v);
case 3: return this.run3(v);
}
}
};
class B {
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(3);
class A {
add(data) {
data.count++;
}
};
class B {
constructor(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();
class A {
add(count) {
return count + 1;
}
};
class B {
constructor(counter) {
this.counter = counter;
this.data = {a:1, count:0};
}
count() {
this.data.count = this.counter.add(this.data.count);
}
};
const b = new B(new A());
b.count();
b.count();
클래스 로직을 작성할 때 낮은 결합도를 고려하기 위해, 해당 로직을 수정할 때 다른 클래스에 영향을 미치는 지 고려하며 작성해야한다.
Coincidental < Logical < Temporal < Procedural < Communicational < Sequential < Functional
class Util {
static isConnect() {}
static log() {}
static isLogin() {}
class Math {
static sin(r) {}
static cos(r) {}
static random() {}
static sqrt(v) {}
}
class App {
init() {
this.db.init();
this.net.init();
this.asset.init();
this.ui.start();
}
};
class Account {
login() {
p = this.ptoken();
s = this.stoken();
if(!s) this.newLogin();
else this.auth(s);
}
};
class Array {
push(V) {}
pop() {}
shift() {}
unshift(v) {}
};
class Account {
ptoken() {
return this.pk || (this.pk = IO.cookie.get('ptoken'));
}
stoken() {
if(this.pk) return this.pk;
if(this.pk) {
const sk = Net.getSessionFromPtoken(this.pk);
sk.then(v => this.sk);
}
}
auth() {
if(this.isLogin) return;
Net.auth(this.sk).then(v => this.isLogin);
}
};
응집도와 결합도는 서로 반비례 관계다. 결합도를 추구하면 응집도를 챙길 수 없고, 응집도를 추구하면 결합도를 챙길 수 없다.