절차지향과 객체지향의 비교
- 자바를 사용한다고 모두 객체지향적으로 코드를 작성한 것은 아니다.
- 자바를 사용하면서도 충분히 절차지향적으로 코드를 작성할 수 있다.
- 절차지향적인 코드가 객체지향적인 코드가 되려면 객체가 단순 전달하는게 아니라 무엇가를 하도록합니다.
- 예를들어, 객체에 어떤 메세지를 전달할 수 있게 됐거나, 객체가 어떤 책임을 지거나, 객체가 어떤 책임을 스스로 처리하는 방법을 알고 있는 등이 있을 수 있습니다.
- 가독성 측면에서 사람과 프로젝트 규모 등 다양한 요소에 따라 객체지향보다 절차지향이 더 가독성이 좋다고 느낄 수 있다.
- 객체지향은 가독성의 측면보다는 책임에 집중하기 때문입니다. 왜냐하면 어떤 객체에 이런 요청을 했을 때, 동작은 알 수 없지만 약속한 규격의 데이터를 반환 할 것이라고 믿기 때문입니다.
- 이러한 규칙이 바로 객체지향을 설명 할 수 있습니다. 내부에 동작을 정확히 모르더라도(캡슐화, 정보 은닉) 어떠한 규격으로 데이터를 반환할 것이라는 약속(추상화)
책임과 역할
- 절차지향도 책임을 분할할 수 있습니다. 절차지향은 함수 단위로 책임을 가질 수 있습니다.
- 절차지향이 객체지향이 될 수 없는 이유는 객체지향에서는 책임을 객체나 함수가 아닌 역할에 책임을 할당합니다. 그리고 이것은 절차지향 언어들에서 지원하지 못하는 기능입니다.
- 여기서 역할을 대표하는게 객체지향에서 흔히 말하는 추상화의 원리를 이용해 다형성을 지원하도록하는 것을 의미합니다.
- 예를들어 쇼핑몰 서비스에서 가격이라는건 중요한 요소입니다. 여기서 ‘지불 해야할 가격을 구하기’을 역할로 정하고 다형성을 이용한다면 개별 상품이든, 상품들이 담긴 장바구니에서도 역할을 기반으로 동작하도록 설정할 수 있습니다.
- 역할에 책임을 할당하는 것은 객체지향에서 아주 중요합니다. 왜냐하면 실제 객체가 무엇인지 중요하지않고 내가 원하는 역할을 할 수 있는 객체라면 모두 사용할 수 있기 때문에 확장에 유연해집니다. 새로운 요구사항이 발생할 경우 그 역할을 할 수 있는 새로운 구현체를 만들면 됩니다.
- 객체지향의 본질은 언어나 문법이 중요하지않고 역할, 책임, 협력이라고 할 수 있습니다.
- 많은 책들에서 객체지향은 실세계를 반영한다고 이야기합니다. 하지만 객체지향이란 자아를 가진 객체들이 서로 협력하는 방식으로 개발되는 것에 가깝습니다.
- 아마 클린아키텍처를 공부해보셨다면 위 내용들이 결국 ‘구체적인 것이 아닌 추상적인 것에 의존하라’ 또는 ‘저수준의 구현보다는 고수준의 정책을 의존하라’를 의미하고 있습니다.
TDA
- 절차지향적인 사고에서 벗어나 객체지향적인 사고를 하기 위한 방법으로 TDA 원칙이 있다.
- Tell, Don’t Ask, 묻지말고 시키도록 한다는 의미입니다. 이 원칙은 객체를 능동적으로 동작하도록 하는 방법입니다.
// Bad-Case
public class Shop {
public void sell(Account account, Product product) {
long price = product.getPrice();
long milleage = account.getMoney();
if (milleage >= price) {
account.setMoney(milleage - price);
System.out.println(product.getName() + "를 구매했습니다.");
} else {
System.out.println("잔액이 부족합니다.");
}
}
}
@Setter
@Getter
class Account {
private long money;
}
@Setter
@Getter
class Product {
private String name;
private long price;
}
// Good-Case
public class Shop {
public void sell(Account account, Product product) {
if (ammount.canAfford(product.getPrice()) {
account.withdraw(product.getPrice());
System.out.println(product.getName() + "를 구매했습니다.");
} else {
System.out.println("잔액이 부족합니다.");
}
}
}
class Account {
private long money;
public boolean canAfford(long amount) {
return money >= amount;
}
public void withdraw(long ammount) {
money -= amount;
}
}
class Product {
private String name;
private long price;
public String getName() {
return name;
}
public long getPrice() {
return price;
}
}
- 위 예제의 Good-Case를 보면 사용자의 잔액을 물어보도록 하는게 아니라(수동적) 사용자가 해당 물건을 구매할 수 있는지 묻는(능동적)으로 동작하고 있습니다.
- 또 TDA는 무분별하게 사용되는 Getter, Setter를 지양하자는 의미도 가지고 있습니다. 실제로 Getter와 Setter는 개발자의 객체지향적 사고를 방해하는 요소가 될 수 있습니다. Getter와 Setter가 무분별하게 사용되는 객체는 Manager나 Util을 이용한 코드를 작성하여 많은 클래스가 생겨납니다.
- TDA는 단순하면서도 객체 지향을 관통하는 원칙입니다. 또한 객체는 마치 자아를 가진 것처럼 움직여야하고 객체는 단순히 데이터를 나르기 위한 수동적인 존재가 아닙니다.
- 객체에게 모든 일을 시킬수는 없습니다. Getter는 상황에 따라서 반드시 필요한 메서드이며 객체에게 일을 최대한 시키려고해도 어딘가에선 협력을 위해서 게터를 사용해야하는 상황이 생길 수 있습니다.