💡 프로그래밍 패러다임

🙆‍♀️ 패러다임의 시대

✔️ 패러다임

  • 모델, 패턴, 전형 적인 예를 의미하는 그리스어인 파라데이그마에서 유래
  • 50년이 지난 현대에서는 한 시대의 사회 전체가 공유 하는 이론이나 방법, 문제 의식 등의 체계를 의미

✔️ 패러다임 전환

과학은 단순한 계단식 발전의 형태가 아닌 새로운 발견이 기존의 과학적 견해를 붕괴시키는 혁명적인 과정을 거쳐 발전

  • 과거의 패러다임이 새로운 패러다임에 의해 대체됨으로써 정상과학의 방향과 성격이 변하는 것을 의미
  • 책에서는 절차형 패러다임에서 객체지향 패러다임으로의 변화를 의미

🙆‍♀️ 프로그래밍 패러다임

✔️ 프로그래밍 패러다임

특정 시대의 어느 성숙한 개발자 공동체에 의해 수용된 프로그래밍 방법과 문제 해결 방법, 프로그래밍 스타일

  • 하지만 프로그래밍 패러다임과거에 있던 패러다임의 형태를 보완하여 만들어지는 발전적 패러다임

✔️ 중요하다고 생각한 이유

  1. 개발자 공동체동일한 프로그래밍 스타일과 모델을 공유 할 수 있게 하므로써 불 필요한 부분에 대한 의견 충돌을 방지

2.객체 지향 패러다임이 제시하는 프로그래밍 패러다임을 설명

  1. 이렇게 함으로써 코드 개발 시 객체 지향 패러다임을 생각하여 개발 하며 동일한 규칙과 표준에 따라 프로그램을 작성

💡 객체, 설계

🙆‍♀️ Intro

✔️ 이론 VS 실무

실무가 어느 정도 발전하고 난 다음에야 비로소 실무의 실용성을 입증할 수 있는 이론이 서서히 그 모습을 갖춰가기 시작하고, 해당 분야가 충분히 성숙해지는 시점에 이르러서야 이론이 실무를 추월하게 된다. - 로버트 L. 글래스 -

✔️ 소프트웨어 에서의 이론 VS 실무

  • 실무에서는 다양한 규모의 SW를 성공적으로 유지보수 하고 있음
  • 하지만 소프트웨어 유지보수와 관련된 효과적인 이론이 발표된 적은 없음
  • 그렇기에 소프트웨어 설계와 유지보수의 중점을 두려면 이론이 아닌 실무에 초점을 맞추는 것이 효과적

🙆‍♀️ 티켓 판매 어플리케이션 구현하기

✔️ 티켓 판매 과정

  1. 소극장은 먼저 관람객 가방 안에 초대장이 있는지 확인
  1. 초대장이 있다면 이벤트에 당첨된 관람객이기에 판매원에게 받은 티켓을 관람객 가방 안에 추가
  1. 초대장이 없다면 티켓을 판매해야 함
  1. 소극장은 관람객의 가방에서 티켓 금액만큼을 차감한 후 매표소 금액 증가
  1. 소극장은 관람객의 가방 안에 티켓을 넣어 줌으로써 관람객의 입장절차 끝냄

✔️ 티켓 판매를 위한 클래스들

Theater

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);
    }
  }
}

🙆‍♀️ 무엇이 문제인가

✔️ 모듈의 3가지 목적

  1. 실행 중에 제대로 동작
  1. 변경을 위해 존재
  1. 코드를 읽는 사람과 의사소통 하는 것
  • 해당 애플리케이션은 제대로 동작하는 것은 만족하지만, 변경에 용이하지 않으며, 코드를 읽는 사람과 의사소통하기 어려운 코드라는 것

✔️ 예상을 빗나가는 코드

다시 한번 enter 메서드가 수행하는 일을 살펴보자.

  • 소극장은 먼저 관람객 가방 안에 초대장이 있는지 확인
  • 초대장이 있다면 이벤트에 당첨된 관람객이기에 판매원에게 받은 티켓을 관람객 가방 안에 추가
  • 초대장이 없다면 티켓을 판매해야 함
  • 소극장은 관람객의 가방에서 티켓 금액만큼을 차감한 후 매표소 금액 증가
  • 소극장은 관람객의 가방 안에 티켓을 넣어 줌으로써 관람객의 입장절차 끝냄
  • 관람객과 판매원이 소극장의 통제를 받는 수동적인 존재 라는 것

    • 현재 판매원매표소 안에 가만히 앉아서 티켓 수량만 확인하는 존재
    • 관람객은 가방만 들고 가며 아무 일도 하지 않음
  • 이해 가능한 코드우리의 예상에서 크게 벗어나지 않아야 하는 것인데, 각 객체의 하는 일우리가 생각하고 있는 것과 다른 범위에서 동작하고 있다는 것

✔️ 변경에 취약한 코드

가장 심각한 문제 Audience와 TicketSeller를 변경하는 경우 Theater도 함께 변경 해야하는 사실

  • 만약 관람객이 가방이 아닌 신용 카드나 현금만 들고 다닌다면 Audience 클래스에서 Bag를 제거할 뿐 아니라, Theater의 enter 메서드도 수정해야 함

  • 이것은 객체 사이의 의존성과 관련된 문제

    • 의존성변경에 대한 영향을 암시
    • 어떤 객체가 변경되면 그 객체에게 의존하는 다른 객체도 함께 변경될 수 있다는 사실
  • 객체 사이의 의존성이 과한 경우결합도가 높다고 말함

객체 지향 설계의 목표는 애플리케이션의 기능을 구현하는데 필요한 최소한의 의존만 유지하며 불필요한 의존성을 제거하며 객체 사이의 결합도를 낮춰 변경이 용이한 설계를 만드는 것이다.

🙆‍♀️ 설계 개선하기

✔️ 자율적인 존재

각 객체에 대해서 본인의 일은 본인이 하도록 규정하는 것

  • Theater는 관람객이 소극장에 입장 하도록 하는 것

  • Audience스스로 가방 안의 현금과 초대장을 처리하는 것

  • TicketSeller스스로 매표소의 티켓과 판매 요금을 다루는 것

TicketSeller가 직접 TicketOffice 처리

public class TicketSeller {
  private TicketOffice ticketOffice;

  public TicketSeller(TicketOffice ticketOffice) {
    this.ticketOffice = ticketOffice;
  }

  public void toSell(Audience audience) {
    if(audience.getBag().hasInvitation()) {
      Ticket ticket = ticketOffice.getTicket();
      audience.getBag().setTicket(ticket);
    } else {
      Ticket ticket = ticketOffice.getTicket();
      audience.getBag().minusAmount(ticket.getFee());
      ticketOffice.plusAmount(ticket.getFee());
      audience.getBag().setTicket(ticket);
    }
  }
}
  • Theater의 enter 메서드에 있는 로직을 TicketSeller 클래스의 toSell 메서드로 새로 생성함으로써 자신의 일을 직접 처리
public class Theater {
  private TicketSeller ticketSeller;

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

  public void enter(Audience audience) {
    ticketSeller.toSell(audience);
  }
}
  • 그렇게 함으로써 Theater에서는 이제 TicketSeller가 하는 일을 직접 처리할 필요가 사라진 것

캡슐화

  • 개념적으로나 물리적으로 객체 내부의 세부적인 사항을 감추는 것
  • 변경하기 쉬운 객체를 만듦으로써, 객체와 객체 사이의 결합도를 낮추는 것
  • 인터페이스와 구현으로 나누고 인터페이스만 공개하는 것을 원칙으로 함

Audience가 직접 bag 처리

public class TicketSeller {
  private TicketOffice ticketOffice;

  public TicketSeller(TicketOffice ticketOffice) {
    this.ticketOffice = ticketOffice;
  }

  public void toSell(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();
    }
  }
}
  • Audience가 직접 bag을 처리하기 때문에 외부에서 Audience가 bag을 소유하고 있다는 사실을 더 이상 알 필요가 없음

  • Theater의 결합도가 줄어든 것을 확인할 수 있음

  • 이는 Audience, TicketSeller가 해야할 일을 직접 하는 자율적인 존재가 되었기 때문

  • 처음 코드 보다 이해하기 쉽고 유연한 설계를 할 수 있는 것을 확인

✔️ 캡슐화와 응집도

객체 내부의 상태캡슐화하고 객체 간에 오직 메시지를 통해서만 상호작용 하도록 하는 것

  • TheaterTicketSeller의 sellTo 메시지를 이해하고 응답 하는 것

  • TicketSellerAudience의 buy 메시지를 이해하고 응답 하는 것

이렇게 밀접하게 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체응집도가 높은 객체라고 함

  • 응집도를 높이기 위해서는 객체 스스로 자신의 데이터를 책임져야 함

  • 즉, 자율적인 존재가 되는 것

휼륭한 객체지향 설계외부의 간섭을 최대한 배제하고 메시지를 통해서만 협력하는 자율적인 객체들의 공동체를 만드는 것

✔️ 절차 지향과 객체 지향

프로세스 & 데이터

  • 프로세스
    • 수정 전 Theater의 enter 메서드
    • 즉, 각 데이터들의 변경, 수정 등이 일어나는 공간
  • 데이터
    • 객체가 가지고 있는 어떠한 값이나 상태

절차 지향

프로세스와 데이터별도의 모듈에 위치시키는 방식
ex) 프로세스 - Theater, 데이터 - Audience, TicketSeller 등

  • 절차적 프로그래밍에서 관람객과 판매원은 단지 수동적인 존재
  • 우리의 예상을 쉽게 벗어날 수 있기 때문원활한 소통이 어려움
  • 변경하기 어려운 코드를 양산하여 버그가 일어나기 쉽다

객체 지향

데이터와 프로세스동일한 모듈 내부에 위치하여 프로그래밍 하는 방식

  • 한 객체 내에서 데이터와 프로세스를 동시에 처리함으로써, 자신의 할 일은 자신이 처리하는 자율적인 존재가 됨
  • 자율적인 존재가 됨으로써, 변경이 쉽고 알아보기 쉬운 코드를 만들 수 있다.

휼륭한 객체지향의 핵심

캡슐화를 이용해 의존성을 적절히 관리함으로써 객체 사이의 결합도를 낮추는 것

✔️ 책임의 이동

책임?

기능을 가리키는 객체지향 세계의 용어

절차 지향에서의 기능 처리

  • 주로 Theater에서 책임이 집중되어 있었다.

  • Theater에서 캡슐화를 통해 ticketSeller에게 책임을 부여함으로써, 책임을 분산시킬 수 있었다.
  • 즉, 각 객체자신이 맡은 일을 스스로 처리함으로써, 책임이 이동한 것

객체지향 설계에서는 각 객체가 할 일자신 스스로 책임진다. 즉, 프로세스와 데이터가 한 모듈에서 모두 처리되는 절차 지향적 설계에서 프로세스와 데이터는 한 객체 내에서 처리함으로써 객체 지향적 설계로 변경할 수 있고 이 과정에서 책임의 이동이 이뤄지는 것이다.

객체 지향의 핵심적절한 객체에 적절한 책임을 할당하는 것이다. 적절한 객체에 적절한 책임을 할당함으로써 이해하기 쉬운 구조와 읽기 쉬운 코드를 얻게 되는 것

✔️ 더 개선할 수 있다

  • Bag, TicketOffice도 자율적인 객체로 만듬
  • 하지만, 자율성이 증가되었지만 결합도와 의존성도 함께 증가

TicketOffice의 자율성 보다는 Audience의 결합도를 낮추는 것이 더 중요하다!

✔️ 그래, 거짓말이다!

  • Bag, TicketOffice는 실세계에선 자율적인 존재가 아님
  • 객체지향 관점에서는 자신의 일을 스스로 처리하는 자율적인 존재

의인화


능동적이고 자율적인 존재SW 객체를 설계하는 원칙

휼륭한 객체지향 설계를 위해선 생명이 없는 수동적인 존재라고 하더라도 객체지향의 세계로 넘어오는 순간 생명과 지능을 가진 싱싱한 존재로 다시 태어난다.

🙆‍♀️ 객체 지향 설계

✔️ 설계가 왜 필요한가?

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

변경 전 코드 & 변경 후 코드

전체적으로 모두 소극장에 방문한 관람객을 입장시키는 작업을 수행하는 것은 동일함

1. 변경 전 코드

  • 데이터와 프로세스를 나누어 별도의 클래스에 모두 배치

2. 변경 후 코드

  • 필요한 데이터보유한 클래스 안에 프로세스를 함께 배치
  • 즉, 서로 다른 설계를 가진 것

좋은 설계오늘 완성해야 하는 기능을 짜야하는 동시에 내일 쉽게 변경할 수 있는 코드를 짜야하는 것, 오늘 요구하는 기능을 온전히 수행하면서 내일의 변경을 매끄럽게 수용할 수 있는 설계

✔️ 객체 지향 설계

객체 지향 프로그래밍의 장점

  • 객체 지향 프로그래밍은 의존성을 효율적으로 통제할 수 있는 방법을 제공함으로써, 요구사항 변경에 좀 더 수월하게 대응할 수 있는 가능성을 보여줌
  • 또한, 코드 변경이라는 측면에서 다른 방법들에 비해 안정감을 줄 수 있다!

객체 지향 패러다임

  • 객체지향 세계에선 애플리케이션은 객체들로 구성되며 애플리케이션의 기능은 객체 간 상호작용을 통해 구현
  • 객체들 사이의 상호작용은 객체 사이의 주고 받는 메서드로 표현
  • 이를 통해 세상을 바라보는 방식 대로 코드를 작성할 수 있다.

✔️ 마무리

휼륭한 객체 지향 설계란 협력 하는 객체 사이 의존성을 적절하게 관리하는 설계다. 세상에 엮인 것이 많은 사람일 수록 변하기 어려운 것처럼 객체가 실행되는 주변 환경에 결합될수록 변경되기 어려워진다.

진정한 객체 지향 설계로 나아가는 길은 협력하는 객체들 사이의 의존성을 조절하여 변경에 용이한 설계를 만드는 것이다!

0개의 댓글