처음 책을 리뷰를 하면서 어떻게 리뷰를 해야될지 고민을 하였다. 이 책의 모든 내용을 정리하기 보다는 내가 느끼기에 책을 읽으면서 역할, 책임, 협력, 메세지
, 추상화
에 대한 내용을 강조를 한다고 생각했다. 그래서 3가지 관점에서 정리하고 마지막에 전체적인 후기를 작성하는 방식으로 리뷰를 하겠다.
물론 아직 완벽하게 이해가 되지 않는 부분이 있기 때문에 정리한 내용이 틀릴 수 있다. 이 부분에 대해서는 책을 다시 읽으면서 작성을 하겠다.
객체지향 프로그래밍(OOP, Object-Oriented Programming)은 소프트웨어 개발 방법론 중 하나로, 현실 세계의 개념을 컴퓨터 프로그램으로 모델링하는 데 중점을 둡니다. 이 방법론은 다음과 같은 주요 개념과 원칙을 기반으로 합니다:
객체는 데이터와 그 데이터를 처리하는 메서드(함수)로 이루어진 독립적인 단위입니다. 예를 들어, 자동차 소프트웨어에서 자동차 객체는 속성(색상, 속도, 모델 등)과 메서드(주행, 정지, 경적 울림 등)를 가질 수 있습니다.
클래스는 객체를 생성하기 위한 설계 도면 또는 템플릿입니다. 클래스는 객체의 속성과 메서드를 정의하고, 객체를 생성하기 위한 청사진 역할을 합니다. 예를 들어, 자동차 클래스는 모든 자동차 객체가 가져야 할 공통 특성과 동작을 정의합니다.
상속은 기존 클래스에서 속성과 메서드를 상속받아 새로운 클래스를 생성하는 개념입니다. 이를 통해 코드 재사용과 계층적 구조를 구현할 수 있습니다. 예를 들어, 자동차 클래스를 기반으로 스포츠카 클래스를 만들 수 있습니다.
다형성은 같은 이름의 메서드나 연산자가 다른 객체에서 다른 방식으로 동작할 수 있는 능력을 가리킵니다. 다형성을 통해 객체 간의 인터페이스를 통일시키고 유연한 코드를 작성할 수 있습니다.
캡슐화는 데이터와 해당 데이터를 처리하는 메서드를 하나의 단위로 묶는 것을 의미합니다. 객체 내부의 상세 구현을 감추고 외부에서는 인터페이스를 통해 접근할 수 있도록 합니다. 이를 통해 코드의 유지보수성과 보안을 향상시킬 수 있습니다.
내가 알던 캡슐화가 아니야....
public class Theater {
private final TicketSeller ticketSeller;
public void enter(Audience audience){
// 잘못된 코드 Case#1
// - Theater가 TicketSeller의 인스턴스 변수 ticketOffice에 직접 접근
ticketSeller.getTicketOffice().getTicket();
// 수정된 코드 Case#1
// - TicketSeller 클래스에 getTicketOffice() 삭제;
ticketSeller.sellTo(audience);
...
}
public class TicketSeller {
private final TicketOffice ticketOffice;
public TicketSeller(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
public void sellTo(Audience a) {
ticketOffice.plusAmount(a.buy(ticketOffice.getTicket()));
}
}
프로젝트를 진행을 하면서 lombok을 사용하면서 어노테이션을 통해 접근이 가능하다. 하지만 이것은 외부에서 접근이 가능 및 변경이 가능하다. 아래의 코드를 살펴보면 @getter와 @setter를 설정을 했다. 이것이 캡슐화? 당연히 아니다. 변수를 private로 설정이 되었지만 사실상 public이다.
캡슐화를 하기 위해서 @setter 제외 및 필요한 부분에 @getter를 작성한다.
// getter, setter를 사용하면 사실상 private으로 설정을 하여도 public과 똑같다.
// 접근하기 위해서는 필요한 부분에 public 인터페이스를 통해서 접근을 해야된다.
@Getter
@Setter
public class Member {
private String name;
private String age;
}
의미 있는 이름 부여: 정적 팩토리 메소드는 이름을 자유롭게 선택할 수 있으므로 객체 생성의 의도를 더 명확하게 표현할 수 있습니다. 이로 인해 코드의 가독성이 향상됩니다.
인스턴스 캐싱: 정적 팩토리 메소드를 통해 객체를 생성하면 필요에 따라 이미 생성된 인스턴스를 재사용할 수 있습니다. 이를 통해 메모리와 성능을 최적화할 수 있습니다.
인터페이스 기반 프로그래밍: 인터페이스를 반환하는 정적 팩토리 메소드를 사용하면 클라이언트 코드가 구체 클래스 대신 인터페이스에 의존하도록 할 수 있습니다.
추가적으로 더 책을 보면서 정리를 하겠다.
객체의 행동과 상태 정의하기 : 협력, 책임, 역할
객체란 위에 설명한 내용과 같이 데이터와 메서드로 이루어진 독립적인 단위입니다. 이때 객체는 상태, 행동, 식별자를 지닌 실체로 상태는 특정 시점에 객체가 가진 정보의 잡합으로 객체의 특징을 표현하며 행동은 외부의 요청에 응답하기 위해 동작하고 반응하는 활동이다.
자유로운 객체
스타벅스
- 스터벅스 cashier라는 객체는 음류 주문을 위한 협력에 참여하고 있고 그 안에
음류 주문을 처리
의 책임을 가지고 있다. 그래서 객체의 행동을 결정하는 것은 객체가 참여하고 있는 협력에 의해 결정되고 상태는 객체가 행동하는데 있어서 필요한 정보에 의해 결정한다.
※ 협력에 참여하기 위해 객체가 가지고 있는 기능
협력에 참여하기 위해 객체가 수행하는 작은 기능으로 객체가 유지해야 하는 정보(상태)와 수행할 수 있는 행동(메서드)에 대한 추상적으로 만든 문장이다.
책임에는 크게 하는 것, 아는 것의 두 가지 범주로 나눌 수 있다.
책임과 메세지
책임(Responsibility)은 객체가 수행하는 일 또는 가지고 있는 역할을 나타내며, 이것은 주로 클래스의 메서드에 해당합니다.
메시지(Message)는 객체 간 상호 작용을 이끄는 수단으로, 객체는 메시지를 통해 다른 객체에게 어떤 작업을 수행하도록 요청합니다. 책임은 객체의 동작과 역할을 정의하고, 메시지는 객체 간의 협력을 촉발하는 통신 수단입니다.
// CoffeeShop 클래스
public class CoffeeShop {
private String name;
private double revenue;
// 커피 주문을 처리하는 메서드 -> 책임의 아는 것
public void takeOrder(Customer customer, String coffeeName) {
// 커피를 만들고 가격을 계산하는 로직을 구현
double price = calculatePrice(coffeeName);
-> 여기서 cutomer에게 메세지를 보낸다.
customer.payForCoffee(price);
revenue += price;
System.out.println(name + "에서 " + customer.getName() + "이(가) " + coffeeName + " 커피를 주문했습니다.");
}
// 커피 가격 계산을 수행하는 메서드
private double calculatePrice(String coffeeName) {...}
}
// Customer 클래스
public class Customer {
private String name;
private double wallet;
// 커피 가격을 지불하는 메서드 -> 책임의 하는 것
public void payForCoffee(double price) {
if (wallet >= price) {
wallet -= price;
System.out.println(name + "이(가) " + price + "달러를 지불했습니다.");
} else {
System.out.println(name + "의 지갑에 돈이 부족합니다.");
}
}
}
역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다.
라고 작성이 되어있다.https://brawny-yellowhorn-474.notion.site/java-OOP-477254766b5440059732a48d0fba2780