getter & setter를 사용하지 말 것
OOP의 특징중 하나인 캡슐화를 배울 때 getter와 setter는 private 필드를 다루기 위한 메서드로 함께 배운다.
but, 객체의 필드에 대한 getter가 존재하는 순간 캡슐화를 해제하고 불변속성을 전역적으로 만들게 된다.
-> 객체를 얻은 어느 곳에서나 getter와 setter를 호출할 수 있게되며 예상치 못한 에러를 발생시킬 수 있다.
pull-based 아키텍쳐
- 데이터를 한 곳으로 가져와 연산을 함
push-based 아키텍쳐
- 데아터를 가져오는 대신 인자로 데이터를 전달함
pull-based 아키텍쳐 예제
class Website {
constructor(private url: string) {}
getUrl() {
return this.url;
}
}
class User {
constructor(private username: string) {}
getUsername() {
return this.username;
}
}
class BlogPost {
constructor(private author: User, private id: string) {}
getId() {
return this.id;
}
getAuthor() {
return this.author;
}
}
function generatePostLink(website: Website, post: BlogPost) {
let url = website.getUrl();
let user = post.getAuthor();
let name = user.getUsername();
let postId = post.getId();
return url + name + postId;
}
generatePostLink 라는 메서드에서 getter를 통해 연산에 필요한 모든 데이터를 불러오는 모습을 볼 수 있다.
push-based 아키텍쳐 예제
class Website {
constructor(private url: string) {}
generateLink(name: string, id: string) {
return this.url + name + id;
}
}
class User {
constructor(private username: string) {}
generateLink(website: Website, id: string) {
return website.generateLink(this.username, id);
}
}
class BlogPost {
constructor(private author: User, private id: string) {}
generateLink(website: Website) {
return this.author.generateLink(website, this.id);
}
}
function generatePostLink(website: Website, post: BlogPost) {
return post.generateLink(website);
}
메서드의 인자로 데이터를 전달해서 각 클래스에서 데이터를 처리후 반환하고 있다.
공통 접사를 사용하지 말 것
계좌에서 돈을 인출하는 예제를 살펴보자.
function accountDeposit(to: string, amount: number) {
let accountId = database.find(to);
database.updateOne(accountId, { $inc: { balance: amount } });
}
function accountTransfer(amount: number, from: string, to: string) {
accountDeposit(from, -amount);
accountDeposit(to, amount);
}
deposit을 직접 호출하는 행위는 좋아보이지 않는다.
deposit을 Account 클래스에 넣어두고, 접근제어자 private을 걸어두어 직접 deposit을 호출하지 못하게 하면 코드는 다음과 같아진다.
class Account {
private deposit(to: string, amount: number) {
let accountId = database.find(to);
database.updateOne(accountId, { $inc: { balance: amount } });
}
transfer(amount: number, from: string, to: string) {
this.deposit(from, -amount);
this.deposit(to, amount);
}
}
이렇게 리팩터링을 진행하면 '메서드는 한 가지 작업만 해야 한다'는 단일 책임 원칙을 지키게 된다.
순서 강제화
A-> B -> C 순서로 실행되어야 하는 작업이 있다고 할 때,
각 작업이 메서드로 이루어져 있어 개발자가 메서드를 잘못된 순서로 호출한다면 에러가 발생할 수 있다.
이를 생성자를 사용해서 순서를 강제할 수 있다.
class Transfer {
constructor(from: string, private amount: number) {
this.depositHelper(from, -this.amount);
}
private depositHelper(to: string, amoubt: number) {
let accountId = database.find(to);
database.updateOne(accountId, { $inc: { balance: amount } });
}
deposit(to: string) {
this.depositHelper(to, this.amount);
}
}
Transfer(송금)클래스를 위와 같이 작성하게 되면 출금 없이 입금이 이루어질 수 없다는 것을 보증할 수 있지만,
수취인(to)을 인자로 deposit을 호출하는 것을 잊어버리면 돈이 그냥 사라질 수 있다.
따라서 입금도 반드시 발생하도록 이 클래스를 다른 클래스로 감쌀 수도 있다.
'책 > 파이브 라인스 오브 코드' 카테고리의 다른 글
5장 - 유사한 코드 융합하기 (0) | 2024.02.17 |
---|---|
4장 - 타입 코드 처리하기 [긴 if 문 리팩터링] (0) | 2024.02.05 |
4장 - 타입 코드 처리하기 [간단한 if 문 리팩터링] (0) | 2024.02.03 |
3장 - 긴 코드 조각내기 for 메서드 다섯 줄 제한(FIVE LINES) (0) | 2024.01.31 |
2장 - 리팩터링 깊게 들여다보기 (1) | 2024.01.29 |