대수적 데이터 타입을 이용한 추상화와 클래스 시스템에 의한 상속의 비교

이전에 ReasonML에 대한 기사을 썼습니다.

그 때는, ReasonML에 있어서의 ADT=Algebraic Data Type(대수적 데이터형) 실장인 Variant

ReasonML의 Variant는 다음과 같으며 Rust라고 Enum에 해당합니다.
type animal =
  | Dog
  | Cat
  | Duck;

이른바 직화라고 하는 것으로, 여러가지 형태의 합으로서 다른 형태를 표현하는 것 같은 느낌입니까.

그래서 이것을 어떻게 사용합니까?
let cry = () => switch(Dog) {
  | Dog => "wan!"
  | Cat => "nyaa!"
  | Duck => "kuwa!!"
}

Js.log(cry()) // ただのconsole.logです。

위의 예라면 wan! 라고 출력됩니다.

설명을 위해서, 이 예를 내 보았습니다만, 아마 그다지 맛을 느끼지 않는다고 생각하기 때문에, 조금 복잡한 예를 냅니다.

아마존 도서 구매



Amazon에서 책을 구입할 때 Kindle(전자책)과 Paperback(종이책)을 선택할 수 있네요.

그래서 두 가지를 모델링하면,

book <-> kindle paperback

라는 is-a 관계가 있는 것을 알 수 있습니다. (kindle is a book. paperback is a book)

(정확하게는, kindle is a kind of book.지도. 뭐 좋은가...)

이 때 book이라는 것은 kindle과 paperback의 추상적인 표현임을 알 수 있습니다.

즉, 아래와 같은 그래프의 상태입니다.



(이쪽 근처에 대해 잘 모르는 분은, 도마미지 영인씨에 의한 논문을 봐 주세요.→ "하늘" 정의 ~ 현대 분석 철학 및 메타 수리적 접근법 .이 분은 어렵다고 생각되고 있는 것을 쉽게 가르치는 천재입니다.)

그럼, 이런 관계를 클래스 시스템에서는 어떻게 표현하고 있었는가 하면,
class Book {
}

class Kindle extends Book {
}

class Paperback extends Book {
}

(JavaScript)

라는 상속을 사용합니다.

그리고, 「전자책에는 할인이 발생한다」라고 하는 사양이 있다고 합니다.

그 경우의 가격 계산의 논리를 생각해 봅시다.
class Book {
}

class Kindle extends Book {
  constructor(price, discount) {
    super()

    this.price = price
    this.discount = discount
  }

  calcPrice() {
    return this.price * (1 - this.discount)
  }
}

class Paperback extends Book {
  constructor(price) {
    super()

    this.price = price
  }

  calcPrice() {
    return this.price
  }
}

되었다고 가정합니다.

그리고, 이 사양에 대해 토론했는데, 「책의 가격 계산은, 일률로 가격×할인율로 한다」라고 정해졌다고 합니다. (어쩐지 무리해...)

앞으로 책의 형태가 늘어나도 마찬가지로 로직으로 대응하고 싶기 때문에 '책'에 관한 일반적인 로직으로 취급하도록 수정했다고 합시다.
class Book {
  constructor(price, discount) {
    this.price = price
    this.discount = discount
  }

  calcPrice() {
    return this.price * (1 - this.discount)
  }
}

class Kindle extends Book {
  constructor(price, discount) {
    super(price, discount) // Bookのコンストラクタの呼び出し
  }
}

class Paperback extends Book {
  constructor(price) {
    super(price, 0) // 紙の本は、割引が発生しないから割引率は「0」
  }
}

const kindle = new Kindle(100, 0.1)

console.log(kindle.calcPrice()) // 90

그럼 Variant에서 이것과 같은 일을하면 어떻게 될 것입니다.

먼저 방금전의 animal 형은 Dog 이나 Cat 의 추상형으로서 기능하고 있는 것을 생각해 주세요.

그 사고방식으로부터 가면,
type book =
| Kindle
| Paperback;

수 있습니다. 또한 Variant는 생성자를 받을 수 있으므로 거기에 가격과 할인율을 설정할 수 있습니다.
type book =
| Kindle(int, float) /* int: 値段 float: 割引率 */
| Paperback(int); /* int: 値段 */

그럼 , class 대신에 이것들을 Book 모듈에 넣어 줍니다.

가격 계산의 논리는 어떻게 될 것입니다.
let calcPrice = (x) => switch(x) {
| Kindle(price, discount) => Js.Int.toFloat(price) *. (1.0 -. discount) /* intのfloat変換が必要 */
| Paperback(price) => price
}

이렇게 쓸 수 있습니다.

이제 이전과 마찬가지로 계산 논리를 일반화하기 위해 클래스가 아닌 Book 모듈을 사용합시다.
Book 모듈에 형식 정의와 공유 논리를 포함시킵니다.
module Book = {
  type book =
  | Kindle(int, float) /* int: 値段 float: 割引率 */
  | Paperback(int); /* int: 値段 */


  let calcPrice = (x) => {
    let calc = (price, discount) => Js.Int.toFloat(price) *. (1.0 -. discount)

    switch(x) {
    | Kindle(price, discount) => calc(price, discount)
    | Paperback(price) => calc(price, 0.0) /* 紙の本は割引なし = 割引率0 */
    }
  }
}

이 모듈을 사용하도록, 방금 전의 구현을 고쳐 보겠습니다.
Js.log(Book.calcPrice(Book.Kindle(100, 0.1))) /* 90 */

처럼 표현할 수 있었습니다.

따라서 ADT는 추상화의 수단으로 사용할 수 있는 것이 아닐까 하는 비교였습니다.

좋은 웹페이지 즐겨찾기