신성한 주말에 오래된 게임인 문명6를 혼자하는, 싱글 게임을 하던 싱글남 키메라...
게임도 이제는 그렇게 막... 재미있지는 않고 오히려 머리쓰는게 더 재미있어졌다.
아무래도 나이를 먹은건가...? ㅠㅠ... 하여간...
최근에 자바/스프링 개발자를 위한 실용주의 프로그래밍 이라는 책을 읽었는데,
객체 지향적인 코드라고 하는 예시를 그냥 보고... 읽다보고 따라도 치지만
뭔가 아직도 성에 안차고 이해가 안된다.
그래서 간간히 상황 부여하면서 연습겸 하나 코드좀 만들어 보려고 한다.
해당 책을 너무 재미있게 읽은 애독자로서 책의 내용도 많이 인용하고 이를 통해서 정말 어떻게 코드를 작성해야
SOLID 원칙에 입각해서, 정말 좋은 코드를 작성할 수 있는지 연습하려고 한다.
글을 적는 순서는 책의 흐름을 많이 참고하려고 한다.
거진 뭐... 복기하고 예제 만드는거지?
그러니 피드백 언제든 환영한다!
혹시나... 나의 미천한(?) 글을 읽게 된다면 꼭! 해당 책을 구매하길 바란다.
해당 코드는 별거 없지만 하단의 주소에 practical_programming이라는 패키지 않에 넣어놧다.
brunch는 study/practical-programming/ex1이다.
https://github.com/thelovemsg/study/tree/study/practical-programming/ex1
1. 상황 설명
당신은 회사에서 공연 좌석을 판매하는 기능을 개발해야 한다.
공연 좌석을 판매하기 위해서는 다양한 상태가 필요하다.
현재 좌석의 상황을 알아야 하고 좌석이 판매가 가능한지도 봐야 하며
좌석이 판매가 되었다가 환불한다면, 환불된 좌석은 다른 사람이 또 구매가 가능하다.
어떻게 보면 좌석 자체를 판매하는 상품으로 바라볼 수 있기 때문에 판매를 관리하는 경우도 상상해보면
외부 결제 시스템과 연동도 할 가능성이 매우 크다. 거래내역도 기록해야겠지?
이거는 나중에 확장하면서 생각해 보자.
2. 초기 코드 작성하기
처음 생각을 하면 우선 어떤 데이터가 필요한지 고민을 한다.
공연 좌석 판매니까... 어떤 공연인지 정보가 있으면 좋겠고 좌석 번호, 좌석이 어느 구역에 속하는지 보고 싶기에
구역도 있을테고 판매 여부와 계산 여부도 알고 싶다.
Conert.java
public class Concert {
private Long concertId;
private List<Seat> seatList;
}
SeatStastus.java & Seat.java
public enum SeatStatus {
PAID, ON_SALE, HOLD, SOLD;
}
public class Seat {
private String seatNo;
private String sectionName;
private SeatStatus seatStatus;
}
ConcertSerivce.java
//@Service
@RequiredArgsConstructor
public class ConcertService {
private final SeatRepository seatRepository;
public long calculateRevenue(long concertId) {
List<Seat> seatList = seatRepository.findByConcertId(concertId);
return seatList
.stream()
.filter(seat -> seat.getSeatStatus().equals(SeatStatus.PAID))
.map(Seat::getOriginPrice).reduce(0L, Long::sum);
}
public long calculateProfits(long concertId) {
List<Seat> seatList = seatRepository.findByConcertId(concertId);
return seatList
.stream()
.filter(seat -> seat.getSeatStatus().equals(SeatStatus.PAID))
.map(s -> s.getOriginPrice() - s.getTaxPrice()).reduce(0L, Long::sum);
}
}
위의 코드들은 콘서트에 좌석이 포함되어 있다는 코드를 작성한 것이다.
콘서드 id로 좌석들을 찾아서, 실제로 결제된 좌석들의 매출과, 실제 판매시 매출을 표현하고 있다.
Java 가 많은 사람들에게 사랑받았던 이유는 무엇인가?
바로 객체지향 코드를 작성할 수 있다는 점이다.
그런 면에서 해당 코드는 접근부터 잘못되었다.
너무 데이터 의존적이며 절차지향 코드이기 때문이다.
여기서 바로 집고 넘어가야 할 점이 있는데,
객체와 객체지향이란 그럼 무엇일까? 그 전에 순차지향은 뭔데? 순차지향과 절차지향은 같은 말인가?
객체란?
조영호씨의 객체지향의 사실과 오해에서 객체는 협력의 참여자라고 표현한다.
객체를 흔히 실제 세계를 표현하는 것이라고 말하는데,
해당 책을 읽어보면 그건 잘못됏다.
행동을 통해 다른 객체와 협업하는 자율적인 존재이며, 하나의 세로운 세상을 창조하는 행위로 보는게 더 타당하다.
우리는 객체에게 메세지를 전달함으로 일을 하도록 요청한다.
Tell, Don't Ask! TDA이다!
순차지향 프로그래밍?
말 그대로 순차적으로 개발 하는 것이다.
순서대로만 실행되도록 개발을 하는 것이다.
순차지향에는 함수의 개념이 존재하지 않고 말 그대로 순차적으로 읽는 대로 코드를 실행한다.
이의 예로 어셈블리어가 있다.
절치지향 프로그래밍?
아니, 절차지향도 순차지향하고 같은 말 아닌가?
아니다! 절차지향 프로그래밍은 함수를 만들어 프로그램을 만드는 함수이다.
복잡한 문제를 개별 함수로 분해하고 개발을 하는 것이다.
소프트웨어 특성상 뭐... 잘만 돌아가면 크게 문제는 없다는것이 문제이기도 하다.
현재 클래스과 객체들이 ConcertService 클래스에 있는 calculateRevenue, calculateProfits처럼 함수를 실행하기 위한 데이터로만 존재를 하지, 객체로 역할을 하지 않기 때문이다.
객체지향 프로그래밍?
그렇다면 객체지향 프로그래밍이란? 위의 객체의 특징을 잘 지키면서 개발을 진행하는 것이다.
그러면 해당 코드들 어떻게 고치는게 좋은가?
객체를 행동을 통해 다른 객체와 협업하는 자율적인 존재로 바라보면서 하려면
객체가 스스로 계산을 해야하지 않을까?
3. 수정 코드 작성하기
Concert.java - 계산을 Concert에서 하도록 수정
public class Concert {
private Long concertId;
private List<Seat> seatList;
public long calculateRevenue() {
return this.seatList
.stream()
.filter(seat -> seat.getSeatStatus().equals(SeatStatus.PAID))
.map(Seat::getOriginPrice).reduce(0L, Long::sum);
}
public long calculateProfits() {
return this.seatList
.stream()
.filter(seat -> seat.getSeatStatus().equals(SeatStatus.PAID))
.map(s -> s.getOriginPrice() - s.getTaxPrice()).reduce(0L, Long::sum);
}
}
ConcertSerivce.java -> TDA! 하도록 코드 개선
package com.example.study.practical_programming.self.good.concert.service;
import com.example.study.practical_programming.self.good.concert.domain.Concert;
import com.example.study.practical_programming.self.good.concert.repository.ConcertRepository;
import lombok.RequiredArgsConstructor;
//@Service
@RequiredArgsConstructor
public class ConcertService {
// private final SeatRepository seatRepository;
private final ConcertRepository concertRepository;
public long calculateRevenue(long concertId) {
Concert concert = concertRepository.findByConcertId(concertId);
return concert.calculateRevenue();
}
public long calculateProfits(long concertId) {
Concert concert = concertRepository.findByConcertId(concertId);
return concert.calculateProfits();
}
}
수정된 코드를 다시 보자.
이제는 객체가 행동을 통해 다른 객체와 협업하는 자율적인 존재로 보이나?
메시지를 TDA 해서 잘 전달이 된거 같지 않나?
이렇게 객체를 데이터 덩어리로 보지 말고 책임을 위임하게 되면 객체지향 스러운 개발을 할 수 있다.
객체지향에 대해 이렇게 잠시 훑었다.
그래도 아직 객체지향으로 개발을 한다는 것이 아직 아리까리하다.
다음에는 좀 더 좋은 예시로 객체지향스럽게 개발하는것이 무엇인지, 그리고 디자인패턴이 자연스럽게
유도가 되는지 공부할 예정이다.
책을 보면 너무 따라한거 같다고?
그렇다. 순전히 내 복습 용이다 ㅎ.
여담이지만, 왜이렇게 맨날 코드책만 보냐 하는 생각도 간혹 들었다.
그런데 그냥 더 좋은 코드를 작성하는 방법을 공부하고 적용하고 싶은 마음이 들어서 그렇다.
그게 전부다 ㅎ
참고 도서
'programming language > Java' 카테고리의 다른 글
[Java] 객체지향 연습 2 - 행위로 코드 설계하기 (0) | 2025.09.22 |
---|---|
[Java] Stream(스트림) - 2탄 (0) | 2025.05.08 |
[Java] 람다 표현식(Lambda Expreesion) - 2탄 (0) | 2025.05.01 |
[Java] 스트림(Stream)이란? - 1탄 (1) | 2025.05.01 |
[Java] 익명 클래스(Anonymous Class) (0) | 2025.04.23 |