본문 바로가기
스터디/오브젝트

오브젝트 01 - 객체, 설계

by 디토20 2024. 2. 6.
반응형

 

 

 

오브젝트 01 - 객체, 설계

 

1.1 티켓 판매 애플리케이션 구현하기

조건

  1. 초대장을 가진 사람은 티켓으로 교환 후 입장 가능
  2. 일반 입장객은 티켓을 구매 후 입장 가능

 

간단하고 예상대로 동작하지만 몇가지 문제점이 있는 프로그래밍

 

public class Theater {
    private TicketSeller ticketSeller;
    
    public Theater(TicketSeller ticketSeller){
    this .ticketSeller = ticketSeller;
    }
    
    public void enter(Audience audience) {
        if (audience.getBag().hasinvitation()) {
            Ticket ticket = ticketSeller.getTicketOffice().getTicket();
            audience.getBag().setTicket(ticket);
        } else {
            Ticket ticket = ticketSeller. getTicketOffice().getTicket() ;
            audience.getBag().minusAmount(ticket.getFee());
            ticketSeller.getTicketOffice().plusAmount(ticket.getFee( ));
            audience.getBag().setTicket(ticket);
    }
}

 

 

 

1.2 무엇이 문제인가?

1.2.1 소프트웨어 모듈이 가져야 하는 세 가지 기능

  1. 실행 중 제대로 동작할 것
    •  모듈의 존재 이유
  2. 변경이 용이할 것
    • 대부분의 모듈은 생명주기 동안 변경되기 때문에 간단한 작업만으로도 변경 가능해야 함
    • 변경하기 어려운 모듈은 제대로 동작해도 개선이 필요
  3. 코드를 읽는 사람과의 의사소통
    • 모듈은 특별한 훈련 없이도 개발자가 쉽게 읽고 이해할 수 있어야 함

 

-> 앞에서 작성한 코드는 제대로 동작하나, 변경 용이성과 가독성이 떨어지므로 수정이 필요하다. 그 이유를 살펴보자

 

 

1.2.2 예상을 빗나가는 코드

1. 수동적인 존재들

  • 현실에서는 관람객이 직접 자신의 가방에서 초대장을 꺼내고, 돈을 직접 꺼내 판매원에게 지불한다.
  • 판매원은 표소에 있는 티켓을 직접 꺼내 관람객에게 건네고 관람객에게서 직접 돈을 받아 매표소에 보관한다.
  • 지만 코드 안의 관람객, 판매원은 그렇게 하지 않는다.
  • 현재의 코드는 우리의 상식과는 너무나도 다르 동작하기 때문에 코드를 읽는 사람과 제대로 의사소통하지 못한다.

2. 복잡한 정보들

  • 이 코드를 이해하기 위해서는 여러 가지 세부적인 내용들을 한꺼번에 기억하고 있어야 한다.
  • Theater의 enter 메서드를 이해하기 위해서는 Audience가 Bag을 가지고 있고, Bag 안에는 현금과 티켓이 들어 있으며 TicketSeller가 TicketOffice에서 티켓을 판매하고, TicketOffice는...등등
  • 하나의 클래스나 메서드에서 너무 많은 세부사항을 다루기 때문에 코드 작성자와 코드를 읽고 이해해야 하는 사람 모두에게 큰 부담.
  • 가장 심각한 문제는 Audience와 TicketSeller를 변경할 경우 Theater도 함께 변경해야 한다는 사실

 

1.2.3 변경에 취약한 코드

  • 세부적인 코드가 바뀌면, 해당 코드를 의존하고 있는 클래스도 전부 변경되어야 함.
  • 최소한의 의존성만 유지하고 불필요한 의존성을 제거하는 것이 목표
  • 의존성 <-> 결합도
    • 의존성이 높을수록 결합도가 높고, 의존성이 낮을 수록 결합도가 낮다!

너무 많은 클래스에 의존하는 theater

 

 

 

1.3 설계 개선하기

- 개선 방법은 간단하다. 관람객과 판매원을 자율적인 존재로 만들자.

 

1.3.1 자율성을 높이자

  • 변경이 어려운 이유는 Theater가 Audience와 TicketSeller뿐만 아니라 Audience의 Bag과 TickeSeller의 TicketOffcie까지 전부 접근할 수 있기 때문.
  • Audience와 TicketSeller가 직접 Bag과 TicketOffice를 처리하는 자율적인 존재가 되도록 설계를 변경하면 해결 된다.
public class Theater {
    private TicketSeller ticketSeller;
    
    public Theater(TicketSeller ticketSeller) {
    	this.ticketSeller = ticketSeller;
    }
    
    public void enter(Audience audience) {
    	ticketSeller.sellTo(audience);
    }
}


public class TicketSeller {
    private TicketOffice ticketOffice;
    
    public TicketSeller(TicketOffice ticketOffice) {
    	this .ticketOffice = ticketOffice;
    }
    
    public void sellTo(Audience audience) {
    	ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket()));
    }
}


public class Audience{
    private Bag bag;
    
    public Audience(Bag bag) {
    this.bag = bag;
    }
    
    public Long buy(Ticket ticket) {
    if (bag.hasinvitation()) {
        bag.setTicket(ticket);
    	return 0L;
    } else {
        bag.setTicket(ticket);
        bag.minusAmount(ticket.getFee());
        return ticket.getFee();
    }
}

 

  • 코드를 수정한결과, 각 클래스 사이의 결합도가 낮아졌다.
  • 또한 내부 구현이 캡슐화됐으므로, 한 클래스의 수정이 다른 클래스에 영향을 미치지 않는다.
  • 캡슐화를 통해 객체와 객체 사이의 결합도를 낮추게 되면 설계를 좀 더 쉽게 변경할 수 있게 된다.

 

1.3.2 무엇이 개선됐는가?

  1. 수정된 코드는 첫번째 코드와 마찬가지고 필요한 기능을 오류없이 수행한다.
  2. 수정된 Audience TicketSeller는 우리의 예상대로 자신이 가지고 있는 소지품을 스스로 관리한다. 코드를 읽는 사람과의 의사소통이 개선되었다.
  3.  Audience TicketSeller 내부 구현을 변경하더라도 Theater 함께 변경할 필요가 없어졌다.

 

 

1.3.3 캡슐화와 응집도

  • 핵심은 객체 내부의 상태를 캡슐화하고 객체들은 단지 메시지를 통해서만 상호작용하도록 하게한다.
  • 자신과 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도(cohesion)가 높다고 말한다.
  • 자신의 데이터를 스스로 처리하는 자율적인 객체를 만들면 결합도를 낮추고 응집도를 높일 수 있다.

 

 

1.3.4 절차지향과 객체지향

절차지향 프로그래밍

  • 수정 전 코드에서는 한 클래스에서 모든 프로세서가 진행됐고 나머지 클래스는 단지 데이터의 역할만 수행했다.
  • 이처럼 프로세스와 데이터를 별도의 모듈에 위치시키는 것을 절차지향 프로그래밍이라고 부른다.
  • 절차지향 프로그래밍은 데이터의 변경으로 인한 영향을 지역으로 고립시키기 어렵고, 이는 변경에 대한 두려움을 증가시킨다.
책임이 중앙집중된 절차지향 프로그래밍

 

객체지향 프로그래밍

  • 데이터와 프로세스가 동일한 모듈 내부에 위치하도록 하는 것을 객체지향 프로그래밍이라고 한다.
  • 각자의 프로세스는 각자의 클래스에만 의존한다.
  • 변경으로 인한 여파가 여러 클래스로 전파되는 것을 효율적으로 억제한다.
  • 캡슐화와 의존성 관리를 통해 객체 사이의 결합도를 낮춰 변경을 용이하게 한다.
책임이 분산된 객체지향 프로그래밍

 

 

 

1.4 객체지향 설계

1.4.1 설계가 왜 필요한가?

  • 설계란 코드를 배치하는 것이다.
  • 좋은설계란 오늘 완성해야 하는 기능을 구현하는 동시에 내일 쉽게 변경할 수 있는 코드다.
  • 요구사항은 항상 변경되므로 코드는 변경이 용이해야 한다.
  • 변경이 용이한 코드란 이해하기 쉬운 코드이다.
  • 코드가 변경에 유연하다고 해도 읽기 어렵다면 코드를 수정하겠다는 마음이 사라질 것이다!

 

 

728x90
반응형

댓글