객체지향이 뭔대 ? '객체지향의 사실과 오해'

DeadWhale·2023년 3월 22일
0

JAVA

목록 보기
10/10

이 글을 적는 이유

예전 부터 "그래서 객체지향이 뭔대?" 라는 질문에 단순히 현실의 객체를 행위와 상태로 나눠 코드로 구현한 것이라고 설명했던 것 같다. 하지만 항상 그 답변에 스스로 만족 못했던 것 같다.

그러던 와중 F-Lab OT에서 객체지향의 사실과 오해라는 책을 받게 되었는데.
선물 받고 몇 주를 안 읽다가 출퇴근 시간을 활용하고 싶어 조금씩 읽기 시작하게 되었다.

책은 굉장히 여러 부분에 대해 적극적으로 객체지향에 대해 비유하고 설명해주지만.
내용을 전부 스포하고 싶지 않기 때문에 몇몇 부분만 정리해 내가 느낀 부분을 정리해 이 글을 작성하려 한다.

책에서 말하는 오해

객체지향의 핵심은 객체가 아닌 객체의 협력과 책임에 있다.

객체지향을 설명할 때 행위와 상태를 가지고 있는 클래스를 중심으로 설명하는 사람이 많을것 같다.
나 또한 누군가 물어보면 클래스-객체를 중심으로 이야기 할 것 같다.

하지만 객체지향의 핵심은 객체가 아닌 협력과 책임에 있다고 말하고 있다.

객체는 하나하나 스스로의 자율성과 책임을 지니고 있고,
객체지향은 그런 객체들간의 협력으로 탄생하는 새로운 세상인 것이다

여기서 자율성과 책임을 이해해보자
우리는 이미 객체란 상태와 행위를 가지고 있는걸 알고 있다.
이걸 해석하자면

객체는

  • 메시지를 전달받는다
  • 자율적으로 행위(메소드)를 수행한다.
  • 객체는 역할을 가지고 그 역할을 처리할 책임을 가진다.

예를 들어 자판기라는 객체가 있다.

이미지로 판단할 수 있는 건
자판기라는 객체와 사람이라는 각각의 자율적인 객체다.

여기서 내가 자판기와 사람이라는 조금 추상적인 비유로 표현했는데
조금더 일반화를 해보자

여기에는 "비타민워터(용과맛)을 먹고 싶은 Actor " 객체와 "여러 종류의 음료를 가지고 있는 롯X 자판기"
두개로 이루어져 있다.

두 객체는 서로 자율적으로 존재하고 서로가 각각의 상태와 행위가 있다.

비타민워터(용과맛)을 먹고 싶은 Actor

public class Actor extends Humans{
    private String 이름;
    private String 마시고_싶은_음료 = "비타민워터_용과맛";
    
    public void 비타민_주스를_요청한다.() {
	  boolean 결과 = LotteVendingMachine.구매하다(음료);
	  
	  if(결과){
		  Actor.마신다()
	  }
    }
    
	private void 마신다(){
		System.out.println(마시고_싶은_음료 + "를 마신다!");
	}
    

여러종류의 음료를 가지고 있는 롯X 자판기

public class LotteVendingMachine {
    private String[] 판매하는_제품들  = { "콜라","사이다","비타민워터(용과맛)","아침햇살" }
    
    // 음료수 구매 메서드
    public boolean 구매하다(String 사고싶은_음료) {
        if (!이_제품이_있나(사고싶은_음료)) {
            System.out.println("해당 음료는 판매하지 않습니다.");
            return false;
        }
        
		System.out.println("해당 음료는 판매하지 않습니다.");
        return true;
    }
    // 음료수 판매 가능 여부 확인 메서드
    private boolean 이_제품이_있나(String 사고싶은_음료) {
        for (String 판매하는_제품 : 판매하는_제품들) {
            if (판매하는_제품.equals(사고싶은_음료)) {
                return true;
            }
        }
        return false;
    }

코드의 생성자와 몇몇 코드는 제외하고 필요한 부분만 간단하게 작성했다.
boolean이라는 값을 전달하지만 음료를 전달했다라고 생각하자.

Actor 객체는 자판기 객체에게 구매한다는 메시지를 날린다.
자판기의 내부에서는 구매한다는 메시지를 받는다
자판기는 음류를 판매하는지 확인 후 판매한다는(true) 메시지를 Actor 에게 보낸다.

이 간단한 흐름에서 메시지와 책임에 집중해보자.

Actor는 자판기에게 마시고 싶은 음료를 알려주는 메시지를 날린다.
자판기는 마시고 싶다는 메시지를 전달받아 그 음료를 줄 수 있는지 확인하는 책임을 지닌다.
좀 더 크게 말하면 자판기는 음료를 판매할 책임을 가지게 된다.

예시에서는 음료의 존재 여부만 확인 했지만 , 실제로는 재고 여부 , 결제확인 외에도 여러 작은 문제들을 확인하고 체크해서 응답해야 하는 책임을 가진다.

하지만 Actor는 이런 책임에 대해 아무것도 모른다 그냥 음료수가 먹고싶을뿐이다.
단순히 돈을 제공하고 , 마시고 싶은 음료 정보를 전달할 뿐이다.

위에서 말한 문장을 다시 확인해보자.

객체지향의 핵심은 객체가 아닌 협력과 책임에 있다고 말하고 있다.

두 객체는 서로 메시지를 전달해 협력을 요청 한다.
두 객체는 서로 하는 일은 모르지만 협력을 통해 목적을 달성 한다.

두 객체간 메시지로만 상호작용이 이루어지며 , 서로 독립적이면서 유기적으로 연결되는 모습을 지니게 되었다.
또한 바로 알겠지만 , 객체의 캡슐화 또한 자연스럽게 이루어진다.

객체가 어떻게 변경되든 결국 메시지를 처리할 수만 있다면 된다.
이를 통해 각각의 객체는 외부의 의존 없이 자신의 역할을 수행 할 수 있다.

종합병원 예를 한번 봐보자.

아픈 사람과 종합 병원이 있을 때.

아픈 사람은

  • 진료를 요청한다( 증상을 전달한다 )
  • 진료를 받는다( )
// 아픈 사람
public class SickPerson {
    public void requestTreatment(String symptoms) {
        // 진료를 요청하고 증상을 전달하는 메소드
    }
    
    public void receiveTreatment() {
        // 진료를 받는 메소드
    }
}

종합 병원 접수처는

  • 진료신청을 받는다(증상을 전달 받는다.)
  • 환자의 증상에 맞는 전문의사에게 연결 한다.
  • 처방여부에 따라 진료비를 청구한다.
// 종합 병원
public class HospitalDesk {
    public void receiveTreatmentRequest(String symptoms) {
        // 진료신청을 받고 증상을 전달받는 메소드
    }
    
    private void doMatched(String symptoms) {
        // 환자의 증상에 맞는 전문의사에게 연결하는 메소드
        callDoctor(symptoms,담당_전문의)
    }
    
    public void chargeTreatmentFee() {
        // 진료비를 청구하는 메소드
    }
}

둘다 각자의 책임과 역할이 존재한다.

환자는 객체로서
1. 진료 요청 책임: 환자는 자신의 진료가 필요한 경우, 병원에 진료를 요청하는 책임이 있다.
2. 정보 제공 책임: 환자는 자신의 증상을 정확하게 전달하여 의사가 진료를 진행하는데 필요한 정보를 제공하는 책임이 있다.
3. 치료 수용 책임: 환자는 의사의 진료를 수용하여 치료를 받는 책임이 있다.

종합 병원 접수처는 객체로서
1. 진료 요청 수용 책임: 병원은 환자로부터 진료 요청을 받아들이는 책임이 있다.
2. 전문의사 연결 책임: 병원은 환자의 증상에 맞는 전문의사를 찾아 연결하는 책임이 있다.
3. 진료비 청구 책임: 병원은 진료비를 청구하여 환자가 적절한 대가를 지불할 수 있도록 하는 책임이 있다.

환자는 병원이 어떤 의사로 연결하는지 모른다 단순히 자신의 증상을 말하고 접수처가 안내하는 담당의한태 간다.
또한 의사가 치료법을 말해줘도 정보를 제공받기 전까지는 어째서 그런 처방이 나왔는지 모른다.
다만 진료해달라는 메시지를 보낼 뿐이다.

접수처는 의사가 어떤 처방을 하는지 몰라도 된다 단순히 환자의 증상을 받고 역할에 맞는 의사에게 전달하면된다.
또한 처방 결과에 상관 없이 ( 상관 있지만 객체로서 예시에서는)
단순히 진료 결과에 따른 병원비만 수납 받으면 된다.

이렇게 역할과 책임이 주어진다면 .
각각의 클래스가 협력을 위해 필요한 행동만 하고 서로간 메시지로서만 협력할 수 있다.

약간 하나둘씩 어색한 부분이 있지만 병원을 예시로 든 이유가 있다.

스프링을 사용하는 모든 개발자는 DispatcherServlet 이라는 객체를 알탠대.
DispatcherServlet은 여러 책임 중 클라이언트에 요청에 맞는 컨트롤러로 전달한 의무를 가진다.

DispatcherServletHandlerMapping 를 이용해서
클라이언트의 Request 기반으로 어떤 Controller가 이 요청을 처리할 수 있는지 찾아주는 역할을 한다.

여기서 다시 한번 객체의 책임을 확인해보자.

  • 클라이언트는 google이라는 요청 메시지를 보낸다 ,
    내부에서 어떠한 일이 발생하는지 알지 못하고 단순히 구글 메인 페이지를 기다린다.
  • DispatcherServlet은 사용자가 어디를 가고싶은지 모른다 다만 이 일을 해결할 수 있는 컨트롤러를 찾아간다.
  • 컨트롤러는 메시지를 받아 메인페이지로 이동시켜주는 정보만 응답한다.
    어떻게 응답한 정보를 클라이언트에게 보내는지 객체는 알지 못한다.

보면 굉장히 건전해 보인다.
1. 각각 역할과 책임을 모두 완수했다.
2. 서로 간 메시지로만 소통 했다.
3. 어느 객체가 어떻게 바뀌는 모든 객체는 본래의 역할과 책임을 수행 할 수 있으면 문제가 없다.


두어가지 비유로 모든걸 설명하기는 아쉬운 글이 맞지만.
객체 상태와 메소드에 집중하기보다 역할과 메시지에 집중해보자는 목적에
글을 작성하게 되었다.

이 글은 책의 내용중 극히 일부만 다룬 내용이다.
상속과 메시지, 추상화등 객체지향을 알면서 내가 오해하던 부분들 많이 해결 해주었다.

이 글에 흥미가 조금이라도 생겼다면 책을 읽어보시길 바랍니다!

객체지향의 사실과 오해

0개의 댓글