[OOP : 오브젝트] 1. 객체, 설계

KIM KYUBIN·2022년 9월 11일
0

OOP

목록 보기
5/7

오브젝트 : 코드로 이해하는 객체지향 설계 - 조영호 저

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

📄 추첨을 통해 선정된 관람객에게 공연을 무료로 관람할 수 있는 초대장을 발송하려고 한다.

  • 이벤트 당첨자에게 발송되는 초대장 구현
public class Invitation {

	// 공연을 관람할 수 있는 초대일자 when
    private LocalDateTime when;
}   
  • 공연을 관람하기를 원하는 모든 사람들은 티켓을 소지하고 있어야만 한다.
public class Ticket {
	
    // 관람비 fee
    private Long fee;
    
    // 관람비를 가져온다.
    public Long getFee() {
    	return fee;
    }
}
  • 이벤트 당첨자는 초대장을, 이벤트에 당첨되지 않은 관람객은 티켓을 사기 위해 현금을 보유하고 있을 것이다.

  • 관람객은 소지품을 보관할 용도로 가방을 들고 올 수 있다고 가정
    - Bag 클래스 추가

  • Bag 인스턴스의 상태는 현금과 초대장을 함께 보관하거나, 초대장 없이 현금만 보관하는 두 가지 중 하나일 것이다.
    - Bag의 인스턴스를 생성하는 시점에 이 제약을 강제할 수 있도록 생성자를 추가

public class Bag {
	
    // 초대장 없이 현금만 보관
    public Bag(long amount) {
    	this(null, amount);
    }
    
    // 현금과 초대장을 함께 보관
    public Bag(Invitation invitation, long amount) {
    	this.invitation = invitation;
        this.amount = amount;
    }
	
    // 현금 amount
    private Long amount;
    // 초대장 invitation
    private Invitation invitation;
    // 티켓 ticket
    private Ticket ticket;
    
    // 초대장 보유 여부 판단
    public boolean hasInvitation() {
    	return invitation != null;
    }
    
    // 티켓 소유 여부
    public boolean hasTicket() {
    	return ticket != null;
    }
    
    // 초대장을 티켓으로 교환
    public void setTicket(Ticket ticket) {
    	this.ticket = ticket;
    }
    
    // 현금 증가
    public void plusAmount(Long amount) {
    	this.amount += amount;
    }
    
    // 현금 감소
    public void minusAmount(Long amount) {
    	this.amount -= amount;
    }
}
  • 관람객이라는 개념을 구현하는 Audience 클래스
public class Audience {

	// 가방 Bag
	private Bag bag;
    
    // 관람객이 소지한 가방
    public Audience(Bag bag) {
    	this.bag = bag;
    }
    
    // 가방의 상태를 가져온다.
    public Bag getBag() {
    	return bag;
    }
}
  • 매표소에는 관람객에게 판매할 티켓과 티켓의 판매 금액이 보관돼 있어야 한다.
public class TicketOffice {
	
    // 판매 금액 amount
    private Long amount;
    // 판매하거나 교환해 줄 티켓의 목록 tickets
    private List<Ticket> tickets = new ArrayList<>();
    
    // 판매 금액, 티켓의 목록 보관
    public TicketOffice(Long amount, Ticket ... tickets) {
    	this.amount = amount;
        this.tickets.addAll(Arrays.asList(tickets));
    }
    
    // 티켓 판매
    // 여기서는 tickets 컬렉션에서 맨 첫 번째 위치에 저장된 ticket 반환
    public Ticket getTicket() {
    	return tickets.remove(0);
    }
    
    // 판매금액 추가
    public void plusAmount(Long amount) {
    	this.amount += amount;
    }
    
    // 판매금액 차감
    public void minusAmount(Long amount) {
    	this.amount -= amount;
    }
}
  • 판매원은 매표소에서 초대장을 티켓으로 교환해 주거나 티켓을 판매하는 역할을 수행한다.
public class TicketSeller {
	
    private TicketOffice ticketOffice;
    
    // 판매원은 자신이 일하는 매표소를 알고 있어야 한다.
    public TicketSeller(TicketOffice ticketOffice) {
    	this.ticketOffice = ticketOffice;
    }
    
    // 자신이 일하는 매표소 반환
	public TicketOffice getTicketOffice() {
    	return ticketOffice;
    }
}

  • 소극장을 구현하는 Theater 클래스
public class Theater {

    private TicketSeller ticketSeller;

    public Theater(TicketSeller ticketSeller) {
        this.ticketSeller = ticketSeller;
    }

    // 관람객을 맞이하는 메소드
    public void enter(Audience audience) {
        // 관람객의 가방 안에 초대장이 들어 있는지 확인
        if (audience.getBag().hasInvitation()) {
            // true면 이벤트 당첨된 관람객
            Ticket ticket = ticketSeller.getTicketOffice().getTicket();
            // 판매원에게서 받은 티켓을 관람객의 가방 안에 넣어준다.
            audience.getBag().setTicket(ticket);
        } else {
            // false면 티켓을 판매해야 한다.
            Ticket ticket = ticketSeller.getTicketOffice().getTicket();
            // 관람객의 가방 안에 티켓 금액만큼 차감
            audience.getBag().minusAmount(ticket.getFee());
            // 매표소에 금액을 증가
            ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
            // 관람객의 가방 안에 티켓을 넣어준다.
            audience.getBag().setTicket(ticket);
        }
    }
}
  • 작성된 프로그램의 로직은 간단하고 예상대로 동작하지만, 문제가 있다.

2. 무엇이 문제인가

모든 소프트웨어 모듈에는 세 가지 목적이 있다.
첫 번째 목적은 실행 중에 제대로 동작하는 것이다. 이것은 모듈의 존재 이유라고 할 수 있다.
두 번째 목적은 변경을 위해 존재하는 것이다. 대부분의 모듈은 생명주기 동안 변경되기 때문에 간단한 작업만으로도 변경이 가능해야 한다.
세 번째 목적은 코드를 읽는 사람과 의사소통하는 것이다. 모듈은 특별한 훈련 없이도 개발자가 쉽게 읽고 이해할 수 있어야 한다. 읽는 사람과 의사소통할 수 없는 모듈은 개선해야 한다. - 로버트 마틴

  • 모듈 : 크기와 상관 없이 클래스나 패키지, 라이브러리와 같이 프로그램을 구성하는 임의의 요소

  • 작성된 프로그램은 첫 번째 목적은 만족하지만 두 번째, 세 번째 목적은 만족시키지 못한다.

2.1 예상을 빗나가는 코드

  • 문제는 관람객과 판매원이 소극장의 통제를 받는 수동적인 존재라는 점이다.
    • 관람객의 입장에서 문제는 소극장이라는 제 3자가 초대장을 확인하기 위해 관람객의 가방을 마음대로 열어 본다는 데 있다.
    • 판매원의 입장에서의 제일 큰 문제는 티켓을 꺼내 관람객의 가방에 집어넣고 관람객에게서 받은 돈을 매표소에 적립하는 일을 소극장이 수행한다는 점이다.
  • 이해 가능한 코드란 그 동작이 우리의 예상에서 크게 벗어나지 않는 코드다.
    • 현재의 코드는 우리의 상식과는 너무나도 다르게 동작하기 때문에 코드를 읽는 사람과 제대로 의사소통하지 못한다.
    • 또, 하나의 클래스나 메서드에서 너무 많은 세부사항을 다루기 때문에 코드를 작성하는 사람뿐만 아니라 코드를 읽고 이해해야 하는 사람 모두에게 큰 부담을 준다.
    • 가장 심각한 문제는, AudienceTicketSeller를 변경할 경우 Theater도 함께 변경해야 한다는 사실이다.

2.2 변경에 취약한 코드

  • 더 큰 문제는 변경에 취약하다는 것이다.

    • 관람객이 가방을 들고 있다는 가정이 바뀐다고 상상해보자.
      • 해당 클래스뿐만 아니라 이 클래스의 의존하는 Theater도 함께 변경해야 한다.
      • 다른 클래스가 Audience의 내부에 대해 더 많이 알면 알수록 Audience를 변경하기 어려워진다.
  • 객체 사이의 의존성(dependency)과 관련된 문제다.

    • 의존성이라는 말 속에는 어떤 객체가 변경될 때 그 객체에게 의존하는 다른 객체도 함께 변경될 수 있다는 사실이 내포돼 있다.
  • 그렇다고 해서 객체 사이의 의존성을 완전히 없애는 것이 정답은 아니다.

    • 우리의 목표는 애플리케이션의 기능을 구현하는 데 필요한 최소한의 의존성만 유지하고 불필요한 의존성을 제거하는 것이다.
  • 객체 사이의 의존성이 과한 경우를 가리켜 결합도(coupling)가 높다고 말한다.

    • 설계의 목표는 객체 사이의 결합도를 낮춰 변경이 용이한 설계를 만드는 것이어야 한다.

[너무 많은 클래스에 의존하는 Theater]

3. 설계 개선하기

  • 코드를 이해하기 어려운 이유는 Theater가 관람객의 가방과 판매원의 매표소에 직접 접근하기 때문이다.
    • 해결 방법은 TheaterAudienceTicketSeller에 관해 너무 세세한 부분까지 알지 못하도록 정보를 차단하면 된다.
    • 관람객과 판매원을 자율적인 존재로 만들면 되는 것이다.

3.1 자율성을 높이자

  • 첫 번째 단계는 Theaterenter 메서드에서 TicketOffice에 접근하는 모든 코드를 TicketSeller 내부로 숨기는 것이다.
public class TicketSeller {

    private TicketOffice ticketOffice;

    // 판매원은 자신이 일하는 매표소를 알고 있어야 한다.
    public TicketSeller(TicketOffice ticketOffice) {
        this.ticketOffice = ticketOffice;
    }

//    // 자신이 일하는 매표소 반환
//    public TicketOffice getTicketOffice() {
//        return ticketOffice;
//    }

	/*
     * Theater의 enter 메서드를 TicketSeller 내부로 이동
     */
    public void sellTo(Audience audience) {
        // 관람객의 가방 안에 초대장이 들어 있는지 확인
        if (audience.getBag().hasInvitation()) {
            // true면 이벤트 당첨된 관람객
            Ticket ticket = ticketOffice.getTicket();
            // 판매원에게서 받은 티켓을 관람객의 가방 안에 넣어준다.
            audience.getBag().setTicket(ticket);
        } else {
            // false면 티켓을 판매해야 한다.
            Ticket ticket = ticketOffice.getTicket();
            // 관람객의 가방 안에 티켓 금액만큼 차감
            audience.getBag().minusAmount(ticket.getFee());
            // 매표소에 금액을 증가
            ticketOffice.plusAmount(ticket.getFee());
            // 관람객의 가방 안에 티켓을 넣어준다.
            audience.getBag().setTicket(ticket);
        }
    }
}
  • TicketSeller에서 getTicketOffice 메서드가 제거됐기 때문에 ticketOffice에 대한 접근은 오직 TicketSeller 안에만 존재하게 된다.
    • 따라서 TicketSellerticketOffice에서 티켓을 꺼내거나 판매 요금을 적립하는 일을 스스로 수행할 수밖에 없다.
  • 개념적이나 물리적으로 객체 내부의 세부적인 사항을 감추는 것을 캡슐화(encapsulation)라고 부른다.
    • 캡슐화를 통해 객체 내부로의 접근을 제한하면 객체와 객체 사이의 결합도를 낮출 수 있기 때문에 설계를 좀 더 쉽게 변경할 수 있게 된다.
public class Theater {

    private TicketSeller ticketSeller;

    public Theater(TicketSeller ticketSeller) {
        this.ticketSeller = ticketSeller;
    }

    // 관람객을 맞이하는 메소드
    public void enter(Audience audience) {
        ticketSeller.sellTo(audience);
    }
}
  • TheaterticketOfficeTicketSeller 내부에 존재한다는 사실을 알지 못한다.

    • 단지 ticketSellersellTo 메시지를 이해하고 응답할 수 있다는 사실만 알고 있을 뿐이다.
  • Theater는 오직 TicketSeller인터페이스(interface)에만 의존한다.

    • TicketSeller가 내부에 TicketOffice 인스턴스를 포함하고 있다는 사실은 구현(implementation)의 영역에 속한다.
  • 객체를 인터페이스와 구현으로 나누고 인터페이스만을 공개하는 것은 객체 사이의 결합도를 낮추고 변경하기 쉬운 코드를 작성하기 위해 따라야 하는 가장 기본적인 설계 원칙이다.

[Theater의 결합도를 낮춘 설계]

  • Theater의 로직을 TicketSeller로 이동시킨 결과, Theater에서 TicketOffice로의 의존성이 제거됐다는 사실을 알 수 있다.
    • TicketOffice와 협력하는 TicketSeller의 내부 구현이 성공적으로 캡슐화된 것이다.
  • Bag 인스턴스에 접근하는 객체가 Theater에서 TicketSeller로 바뀌었을 뿐 Audience는 여전히 자율적인 존재가 아닌 것이다.
    • Bag에 접근하는 모든 로직을 Audience 내부로 감추기 위해 Audiencebuy 메서드를 추가하고 TicketSellersellTo 메서드에서 setBag 메서드에 접근하는 부분을 buy 메서드로 옮기자.
public class Audience {

    // 가방 Bag
    private Bag bag;

    // 관람객이 소지한 가방
    public Audience(Bag bag) {
        this.bag = bag;
    }

    // Ticket을 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();
        }
    }
}
  • Audience는 자신의 가방 안에 초대장이 들어있는지를 스스로 확인한다.
    • Bag의 존재를 내부로 캡슐화할 수 있게 됐다.
  • TicketSellerAudience의 인터페이스에만 의존하도록 수정하자.
    • TicketSellerbuy 메서드를 호출하도록 코드를 변경하면 된다.

[자율적인 Audience와 TicketSeller로 구성된 설계]

  • 코드를 수정한 결과, TicketSellerAudience 사이의 결합도가 낮아졌다.
    • 또한 내부 구현이 캡슐화됐으므로 Audience의 구현을 수정하더라도 TicketSeller에는 영향을 미치지 않는다.
  • AudienceTicketSeller가 내부 구현을 외부에 노출하지 않고 자신의 문제를 스스로 책임지고 해결하는 자율적인 존재가 됐다.

3.2 무엇이 개선됐는가

  • 수정된 예제 역시 관람객들을 입장시키는데 필요한 기능을 오류 없이 수행한다.

  • 수정된 AudienceTicketSeller는 자신이 가지고 있는 소지품을 스스로 관리한다.

  • AudienceTicketSeller의 내부 구현을 변경하더라도 Theater를 함께 변경할 필요가 없어졌다.

3.3 어떻게 한 것인가

  • 판매자가 티켓을 판매하기 위해 TicketOffice를 사용하는 모든 부분을 TicketSeller 내부로 옮기고, 관람객이 티켓을 구매하기 위해 Bag을 사용하는 모든 부분을 Audience 내부로 옮긴 것이다.

    • 자기 자신의 문제를 스스로 해결하도록 코드를 변경한 것이다.

3.4 캡슐화와 응집도

  • 핵심은 객체 내부의 상태를 캡슐화하고 객체 간에 오직 메시지를 통해서만 상호작용하도록 만드는 것이다.

  • 밀접하게 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도(cohesion)가 높다고 말한다.

    • 자신의 데이터를 스스로 처리하는 자율적인 객체를 만들면 결합도를 낮출 수 있을뿐더러 응집도를 높일 수 있다.
  • 외부의 간섭을 최대한 배제하고 메시지를 통해서만 협력하는 자율적인 객체들의 공동체를 만드는 것이 훌륭한 객체지향 설계를 얻을 수 있는 지름길인 것이다.

3.5 절차지향과 객체지향

  • Audience, TicketSeller, Bag, TicketOffice는 관람객을 입장시키는 데 필요한 정보를 제공하고 모든 처리는 Theaterenter 메서드 안에 존재했었다는 점에 주목하라.

    • Theaterenter 메서드는 프로세스(Process)이며 Audience, TicketSeller, Bag, TicketOffice데이터(Data)다.
  • 프로세스와 데이터를 별도의 모듈에 위치시키는 방식을 절차적 프로그래밍(Procedural Programming)이라고 부른다.

  • 맨 처음의 Theater는 절차적 프로그래밍 방식으로 작성된 코드의 전형적인 의존성 구조를 보여준다.

    • 프로세스를 담당하는 TheaterAudience, TicketSeller, Bag, TicketOffice 모두에 의존하고 있음에 주목하라.
  • 일반적으로 절차적 프로그래밍은 우리의 직관에 위배된다.

    • 절차적 프로그래밍의 세상은 우리의 예상을 너무나도 쉽게 벗어나기 때문에 코드를 읽는 사람과 원활하게 의사소통하지 못한다.
  • 더 큰 문제는 절차적 프로그래밍의 세상에서는 데이터의 변경으로 인한 영향을 지역적으로 고립시키기 어렵다는 것이다.

    • Audience, TicketSeller, Bag, TicketOffice 가운데 하나라도 변경될 경우 Theater도 함께 변경해야 한다.
  • 해결 방법은 자신의 데이터를 스스로 처리하도록 프로세스의 적절한 단계를 AudienceTicketSeller로 이동시키는 것이다.

  • 데이터와 프로세스가 동일한 모듈 내부에 위치하도록 프로그래밍하는 방식을 객체지향 프로그래밍(Object-Oriented Programming)이라고 부른다.

  • 훌륭한 객체지향 설계의 핵심은 캡슐화를 이용해 의존성을 적절히 관리함으로써 객체 사이의 결합도를 낮추는 것이다.

3.6 책임의 이동

  • 두 방식 사이의 근본적인 차이를 만드는 것은 책임의 이동(shift of responsibility)이다.

  • 두 방식의 차이점을 가장 쉽게 이해할 수 있는 방법은 기능을 처리하는 방법을 살펴보는 것이다.

    • 처음에는 책임이 Theater에 집중되어 있었다.

    • 마지막에는 하나의 기능을 완성하는 데 필요한 책임이 여러 객체에 걸쳐 분산되어 있다.

      • Theater에 몰려 있던 책임이 개별 객체로 이동하여 책임의 이동이 일어났다.
  • 객체가 어떤 데이터를 가지느냐보다는 객체에 어떤 책임을 할당할 것이냐에 초점을 맞춰야 한다.

  • 설계를 어렵게 만드는 것은 의존성이라는 것을 기억하라.

    • 해결 방법은 불필요한 의존성을 제거함으로써 객체 사이의 결합도를 낮추는 것이다.
    • 불필요한 세부사항을 객체 내부로 캡슐화하는 것은 객체의 자율성을 높이고 응집도 높은 객체들의 공동체를 창조할 수 있게 한다.

3.7 더 개선할 수 있다

public class Audience {

    // 가방 Bag
    private Bag bag;

    // 관람객이 소지한 가방
    public Audience(Bag bag) {
        this.bag = bag;
    }

    // Ticket을 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();
        }
    }
}
  • 현재의 Bag은 과거의 Audience처럼 스스로 자기 자신을 책임지지 않고 Audience에 의해 끌려다니는 수동적인 존재다.
  • Bag의 내부 상태에 접근하는 모든 로직을 Bag 안으로 캡슐화해서 결합도를 낮춰서 자율적인 존재로 바꿔보자.
public class Bag {

    // 현금 amount
    private Long amount;
    // 초대장 invitation
    private Invitation invitation;
    // 티켓 ticket
    private Ticket ticket;

    public Long hold(Ticket ticket) {
        if (hasInvitation()) {
            setTicket(ticket);
            return 0L;
        } else {
            setTicket(ticket);
            minusAmount(ticket.getFee());
            return ticket.getFee();
        }
    }

    // 초대장 보유 여부 판단
    private boolean hasInvitation() {
        return invitation != null;
    }

    // 초대장을 티켓으로 교환
    private void setTicket(Ticket ticket) {
        this.ticket = ticket;
    }

    // 현금 감소
    private void minusAmount(Long amount) {
        this.amount -= amount;
    }
}
  • Bag의 구현을 캡슐화시켰으니 이제 AudienceBag의 구현이 아닌 인터페이스에만 의존하도록 수정하자.
public class Audience {

    // 가방 Bag
    private Bag bag;

    // 관람객이 소지한 가방
    public Audience(Bag bag) {
        this.bag = bag;
    }

    public Long buy(Ticket ticket) {
        return bag.hold(ticket);
    }
}
  • 현재의 TicketSellerTicketOffice에 있는 Ticket을 마음대로 꺼내서는 자기 멋대로 Audience에게 팔고 Audience에게 받은 돈을 마음대로 TicketOffice에 넣어버린다.
public class TicketSeller {

    private TicketOffice ticketOffice;

    public void sellTo(Audience audience) {
        ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket()));
    }
}
  • 잃어버린 TicketOffice의 자율권을 찾아주자
    • TicketOfficesellTicketTo 메서드를 추가하고 TicketSellersellTo 메서드의 내부 코드를 이 메서드로 옮기자.
public class TicketOffice {

    // 판매 금액 amount
    private Long amount;
    // 판매하거나 교환해 줄 티켓의 목록 tickets
    private List<Ticket> tickets = new ArrayList<>();

    // 판매 금액, 티켓의 목록 보관
    public TicketOffice(Long amount, Ticket ... tickets) {
        this.amount = amount;
        this.tickets.addAll(Arrays.asList(tickets));
    }
    
    public void sellTicketTo(Audience audience) {
        plusAmount(audience.buy(getTicket()));
    }

    // 티켓 판매
    // 여기서는 tickets 컬렉션에서 맨 첫 번째 위치에 저장된 ticket 반환
    private Ticket getTicket() {
        return tickets.remove(0);
    }

    // 판매금액 추가
    private void plusAmount(Long amount) {
        this.amount += amount;
    }
}
  • 이제 TicketSellerTicketOffice의 구현이 아닌 인터페이스에만 의존하게 됐다는 점이다.
public class TicketSeller {

    private TicketOffice ticketOffice;

    public void sellTo(Audience audience) {
        ticketOffice.sellTicketTo(audience);
    }
}
  • 이 변경은 그다지 만족스럽지 않다.
    • TicketOfficeAudience 사이에 의존성이 추가됐기 때문이다.
    • TicketOfficeAudience에게 직접 티켓을 판매하기 때문에 Audience에 관해 알고 있어야 한다.
  • 어떤 기능을 설계하는 방법은 한 가지 이상일 수 있다.
  • 어떤 경우에도 모든 사람들을 만족시킬 수 있는 설계를 만들 수는 없다.

3.8 그래, 거짓말이다!

  • 무생물 역시 스스로 행동하고 자기 자신을 책임지는 자율적인 존재로 취급했다.

    • 객체지향의 세계에 들어오면 모든 것이 능동적이고 자율적인 존재로 바뀐다.
  • 능동적이고 자율적인 존재로 소프트웨어 객체를 설계하는 원칙을 가리켜 의인화(anthropomorphism)라고 부른다.

4. 객체지향 설계

4.1 설계가 왜 필요한가

설계란 코드를 배치하는 것이다.

  • 설계는 코드를 작성하는 매 순간 코드를 어떻게 배치할 것인지를 결정하는 과정에서 나온다.

  • 우리는 오늘 완성해야 하는 기능을 구현하는 코드를 짜야 하는 동시에 내일 쉽게 변경할 수 있는 코드를 짜야 한다.

    • 요구사항이 항상 변경되기 때문이다.
    • 코드를 변경할 때 버그가 추가될 가능성이 높기 때문이다.

4.2 객체지향 설계

  • 객체지향 프로그래밍은 의존성을 효율적으로 통제할 수 있는 다양한 방법을 제공함으로써 요구사항 변경에 좀 더 수월하게 대응할 수 있는 가능성을 보여준다.

  • 변경 가능한 코드란 이해하기 쉬운 코드다.

  • 세상에 존재하는 모든 자율적인 존재처럼 객체 역시 자신의 데이터를 스스로 책임지는 자율적인 존재다.

  • 객체지향의 세계에서 애플리케이션은 객체들로 구성되며 애플리케이션의 기능은 객체들 간의 상호작용을 통해 구현된다.

    • 객체들 사이의 상호작용은 객체 사이에 주고 받는 메시지로 표현된다.
  • 애플리케이션의 기능을 구현하기 위해 객체들이 협력하는 과정 속에서 객체들은 다른 객체에 의존하게 된다.

    • A가 B에 메시지를 전송하기 위해서는 B에 대해 알고 있어야 한다.
  • 훌륭한 객체지향 설계란 협력하는 객체 사이의 의존성을 적절하게 관리하는 설계다.

profile
상상을 현실로 만들기 위해 노력하는 개발자

0개의 댓글