공부하는 블로그

#7 - 분석: 내가 만든 소프트웨어를 실제 세상으로 (2) 본문

design patterns

#7 - 분석: 내가 만든 소프트웨어를 실제 세상으로 (2)

devtimothy 2019. 3. 29. 07:46

#7 - 분석: 내가 만든 소프트웨어를 실제 세상으로(2)

본 블로깅은 Head first OOAD: 세상을 설계하는 객체지향 방법론 (한빛미디어) 책을 Typescript 문법으로 전환하며 공부하는 글입니다.
글을 읽기 전에, 광고 배너 한번씩만 클릭 부탁드립니다. 블로그 운영에 큰 보탬이 됩니다 :)

코드와 메소드 분석

주 경로

  1. 주인 강아지가 밖에 나가려고 짖는다.
  2. 강아지 소리 인식기가 강아지 소리를 "듣는다" - BarkRecognizer.recognize, Bark
  3. 주인 강아지가 짖으면 강아지 소리 인식기가 여는 요청을 강아지 문에 보낸다. - BarkRecognizer.recognize, open(), allowedBarks
  4. 강아지 문이 열린다. - DogDoor, open()
  5. 주인 강아지가 밖으로 나간다.
  6. 주인 강아지가 화장실을 이용한다.
    1. 문이 자동으로 닫힌다 - DogDoor, close()
    2. 주인 강아지가 들여보내달라고 짖는다
    3. 강아지 소리 인식기가 강아지 소리를 "듣는다" - BarkRecognizer.recognize, Bark
    4. 주인 강아지가 짖으면, 강아지 소리 인식기가 여는 요청을 문에 보낸다. - BarkRecognizer.recognize, open(), allowedBarks
    5. 강아지 문이 열린다. - DogDoor, open()
  7. 주인 강아지는 안으로 들어온다.
  8. 문이 자동으로 닫힌다. - DogDoor, close()

대체 경로들

2.1. 주인이 강아지가 짖는 것을 듣는다.

3.1. 주인이 리모컨의 버튼을 누른다. - Remote.pressButton()

6.3.1. 주인이 강아지가 짖는 것을 듣는다.

6.4.1. 주인이 리모컨의 버튼을 누른다.- Remote.pressButton()

클래스 다이어그램 구성

클래스 다이어그램은 ERD와 비슷한 것 같다. RDB 구성을 할 때에도 관계도를 그리면서 하는데, 클래스 다이어그램은 말그대로 클래스 간의 관계를 나타내는 것을 말하는 듯 하다.

다이어그램을 나타낼 때 세개의 칸으로 구분짓는데, 맨 위에는 클래스명, 가운데는 속성, 그 아래는 메소드를 표기한다.

클래스간의 관계는 화살표로 나타내는데, 참조, 확장, 상속 등을 표현할 수 있다.

아래 그림에서는 Remote 클래스가 DogDoor 클래스를 향해서 화살표가 그어져 있다. 보통은 연관을 나타내는 속성은 다이어그램에 기입하지 않는다. (Remote에 DogDoor 속성을 써넣지 않았다.)

화살표 옆에 숫자는 연관의 다중도를 말한다. ERD로 말하자면 1:1, 1:N, N:M 등의 관계를 말하는 것이다. 아래 그림에서는 door 속성이 하나의 DogDoor 객체를 저장한다. DogDoor에서 Bark를 향하는 화살표에는 * 이 표시되어 있다. 많이 담을수 있다는 이야기이다.

[그림]

머릿속에 담아놓고 말로 설명하는 것보다는 그림으로 한번에 표현하는 것이 훨씬 쉽다. 나만 알고 코드를 짜는 것은 도움이 되지 않는다. 동료들과 협업할 때도 구조를 잘 작성한 후에 만들면 훨씬 좋다. 왜냐하면 일을 분담 할 수 있기 때문이다. 코드 작성하다가 설계가 잘못된 것을 알고서 되돌리면서 허비하는 시간보다, 다이어그램을 만들며 설계를 한 후에 코드를 작성하는 것이 훨씬 좋다.

어제 나의 경우에도 알림톡 서비스 리팩토링을 하면서 DTO와 메시지 전송 단을 나누어 작업하면서 동료와 업무를 나누어서 작업할 수 있어 좋았다.

물론 클래스 다이어그램만으로는 부족하다. 타입 정보도 부족하고, 메소드를 어떻게 작성해야 하는지도 알 수 없다. 클래스 다이어그램은 전체적인 외형와꾸을 구성한다고 생각하면 될 것이다.

코드 구현

이제 코드 구현을 해보자. 오래 기다렸다.

import { DogDoor } from "./DogDoor";
import { Bark } from "./Bark";
export type BarkRecognizer = InstanceType<typeof BarkRecognizer>;
export const BarkRecognizer = class {
  private _door: DogDoor;
  constructor(dogDoor: DogDoor) {
    this._door = dogDoor;
  }
  public recognize(bark: Bark): void {
    console.log(`BarkRecognizer: Heard a ${bark.sound}`);
    this._door.allowedBarks.forEach((allowedBark: Bark) => {
      if (allowedBark.equals(bark)) {
        this._door.open();
      }
    });
  }
};
import { Bark } from "./Bark";

export type DogDoor = InstanceType<typeof DogDoor>;
export const DogDoor = class {
  private _open: boolean;
  private _allowedBarks: Bark[];

  constructor() {
    this._open = false;
    this._allowedBarks = [];
  }

  public open(): void {
    console.log("The dog door opens.");
    this._open = true;
    setTimeout(() => {
      this.close();
    }, 5000);
  }

  public close(): void {
    console.log("The dog door closes.");
    this._open = false;
  }

  public isOpen(): boolean {
    return this._open;
  }

  get allowedBarks(): Bark[] {
    return this._allowedBarks;
  }

  addAllowedBark(allowedBark: Bark) {
    this._allowedBarks.push(allowedBark);
  }
}
import { DogDoor } from "./src/DogDoor";
import { Remote } from "./src/Remote";
import { BarkRecognizer } from "./src/BarkRecognizer";
import { Bark } from "./src/Bark";
export class DogDoorSimulator {
  public main(): void {
    const door: DogDoor = new DogDoor();
    door.addAllowedBark(new Bark("rowlf"));
    door.addAllowedBark(new Bark("roowlf"));
    door.addAllowedBark(new Bark("rawlf"));
    door.addAllowedBark(new Bark("roof"));
    const recognizer: BarkRecognizer = new BarkRecognizer(door);
    const remote: Remote = new Remote(door);

    console.log("Fido barks to go outside...");
    recognizer.recognize(new Bark("rowlf"));

    console.log("\nFido has gone outside...");

    setTimeout(() => {
      console.log("\nFido's all done...");
      console.log("...but he's stuck outside!");

      const smallDogBark: Bark = new Bark("yip");
      console.log("A small dog starts barking...");
      recognizer.recognize(smallDogBark);

      setTimeout(() => {
        console.log("\nFido starts barking...");
        recognizer.recognize(new Bark("roowlf"));
        console.log("\nFido's back inside...");
      }, 5000)
    }, 10000);
  }
}

new DogDoorSimulator().main();

동작시켜보면 코드가 매우 잘 도는 것을 알 수 있다. 이렇게해서 소프트웨어 분석에 대해서 알아보았다. 다음 시간에는 좋은 디자인이란 무엇인가에 대해 고민해보도록 하자.

Comments