// 객체
public class Product {
// 데이터
private String name;
private long price
private long quantity;
// 프로시저
public void increaseQuntity() {
this.quantity++;
};
}
객치제향 프로그램은 객체들의 상호관계로 구성된다.
객체는 자신만의 기능을 제공하며, 각 객체들은 서로 연결되어 메세지를 주고 받으며 소프트웨어를 구성한다.
객체
객체는 데이터와 데이터를 조작하는 프로시저(메소드)로 구성된다.
객체는 객체마다 자신만의 책임(Responsibility)이 있다.
객체는 서로의 책임을 하며 다른 객체와 의존(Dependency)하며 소프트웨어를 구성한다.
‘읽기 객체’ , ‘집계 객체’ , ‘정산 객체’는 각자의 책임을 가지며 서로 메시지를 주고 받으며 의존 하고 있다.
객체 책임
객체가 가지는 책임을 결정하는 것이 객체 지향 설계의 출발점이다.
매출 데이터 정산에 대한 기능 목록 정리
업주의 매출 데이터를 읽는다.
매출 데이터를 업주별로 집계한다.
매출 데이터를 업주에게 정산한다.
전체 흐름을 제어한다.
// 전체 흐름제어
public class SalesFlowService {
private SalesInfoRepository salesInfoRepository;
private SettleRepsitory settleRepository;
public void calculateSalesData(LocalDate txDate) {
//1. 업주의 매출 데이터를 읽어온다.
List<SalesInfo> salesInfos = salesInfoRepository.findByTxDate(txDate);
// 2. 매출데이터를 업주별로 집계한다.
Map<Long, List<SalesInfo>> salesInfoByShopNumber = salesInfos.stream().collect(groupingBy(SalesInfo::shopNumber));
// 3. 매출데이터를 업주에게 정산한다.
for (String shopNumber : salesInfoByShopNumber.keySet()) {
List<SalesInfo> specificShopSalesInfos = salesInfoByShopNumber.get(shopNumber);
List<SettleDate> specificShopSettleDatas = specificShopSalesInfos.stream().map(SettleDate::convert).collect(toList());
settleRepository.save(specificShopSettleDatas);
}
}
}
흐름 제어 객체는 매출 데이터 읽기, 집계, 정산 객체의 기능을 이용하여 자신의 기능을 완성했다.
객체에서 다른 객체를 생성 하거나 다른 객체의 메서드를 호출할 때, 해당 객체에 의존 한다고 한다.
위 코드에서 SalesFlowService는 SalesInfoReader, SalesInfoCalculator, SettleDataWriter 에 의존한다.
의존성 전이
객체의 변화로 인해 다른 객체에 영향이 가게 된다.
변경이 많은 객체는 의존이 많이 되지 않도록 해야 한다.
변경은 의존 관계를 따라 전이 된다.
public class Restaruant {
private Customer customer;
private Food food;
public void sellFood() {
int amount = customer.buyFood(food);
}
}
public class Customer {
private long fund;
private Food food;
public int buyFood(Food food) {
fund = fund - food.caculateAmount();
food = food;
}
}
public class Food {
private int price;
private int quantity;
public int calculateAmount() {
return price * quantity;
}
}
Food 의 변화는 Customer 에게 영향을 미치며 Customer의 변화는 Restaruant 에 변화를 미친다.
캡슐화
객체가 내부적으로 기능을 어떻게 구현 했는지 감추는 것을 말한다.
캡슐화를 통해 내부구현의 변경이 그 기능을 사용하는 코드에 영향을 받지 않게한다.
public class Bank {
public void getFirstDigitOfAfterRegistrationNumber(Customer customer) {
if (before 2000) {
if(customer.isMale) {
return 1;
}
return 2;
} else {
// 3, 4
}
}
}
public class Company {
public void getFirstDigitOfAfterRegistrationNumber(Customer customer) {
if(customer.isMale) {
return 1;
}
return 2;
}
}
public class Customer {
private LocalDate birthDate;
private boolean male;
public boolean isMale() {
return male;
}
public LocalDate getBirthDate() {
return birthDate;
}
}
캡슐화가 되지 않은 코드는 내부구현이 외부로 노출된다.
주민등록번호 첫째자리를 구하는 로직이 2000년대생 이후로 남자는 ‘3’ 여자는 ‘4’로 변경 되었다면 Customer 의 데이터를 사용하는 Bank 와 Company 코드는 코드 수정이 발생한다.
public class Bank {
public int getFirstDigitOfAfterRegistrationNumber(Customer customer) {
return customer.getFirstDigitOfAfterRegistrationNumber();
}
}
public class Company {
public int getFirstDigitOfAfterRegistrationNumber(Customer customer) {
return customer.getFirstDigitOfAfterRegistrationNumber();
}
}
public class School {
public int getFirstDigitOfAfterRegistrationNumber(Customer customer) {
return customer.getFirstDigitOfAfterRegistrationNumber();
}
}
public class Customer {
private LocalDate birthDate;
private boolean male;
public int getFirstDigitOfAfterRegistrationNumber(Customer customer) {
if (birthDate.isAfter(LocalDate.of(2000,1, 1)) {
return after2000FirstDigitOfAfterRegistrationNumber();
} else {
return before2000FirstDigitOfAfterRegistrationNumber();
} else if {
return //5, 6
}
}
private int after2000FirstDigitOfAfterRegistrationNumber() {
if(customer.isMale) {
return 3;
}
return 4;
}
private int before2000FirstDigitOfAfterRegistrationNumber() {
if(customer.isMale) {
return 1;
}
return 2;
}
}
캡슐화된 코드는 내부구현을 감춤으로써 변경을 외부로 전파하지 않는다.
캡슐화를 통해서 Customer의 구현이 변경되어도 이를 사용하는 외부 구현에는 영향을 미치지 않는다.