Upgrade to Pro — share decks privately, control downloads, hide ads and more …

どうして僕の作ったクラスが手続き型と言われなきゃいけないんですか

Goto Akiko
November 01, 2024

 どうして僕の作ったクラスが手続き型と言われなきゃいけないんですか

-若手プログラマーのオブジェクト指向の疑問に答えていく 第2弾-

ある帳票のシステムを作ることになったので、オブジェクト指向を使ってプログラミングした。
しかし、上司に
「こんな手続き型プログラミングじゃダメだよ。」
と言われてしまう。
ちゃんとクラスにしているし、プロパティも隠蔽できているのに、なぜか。

<こんな人向け>
・プログラミングを始めて2,3年以上
・オブジェクト指向でプログラミングをしている
・オブジェクト指向のプログラミングを理解はできるし、自分で書いているが、うまく書けているか自信がない

Goto Akiko

November 01, 2024
Tweet

More Decks by Goto Akiko

Other Decks in Programming

Transcript

  1. 5

  2. 7 コードはTypeScriptで書いていきます。 // 納品書を作成する makeDeliverySlip(): void { const line =

    `商品名:${this.item.name} 数量:${this.item.quantity} 納品日:${this.shipping_date}` this.printToPdf(line) } printToPdf(line:String): void { console.log(line) // PDF出力処理は割愛 } } export type DocumentType = 'invoice' | 'deliverySlip' export type Item = { name: string price: number quantity: number } //エントリーポイント index.ts import {DocumentMaker, Item} from "./procedure/DocumentMaker"; const item:Item = { name: '商品A', price: 1000, quantity: 1 } const documentMaker = new DocumentMaker('invoice', item, 1) documentMaker.makeDocument() export class DocumentMaker { private readonly type: DocumentType private readonly item: Item private readonly shipping_date: Date = new Date() constructor(type:DocumentType, item:Item) { this.type = type this.item = item } formatDateToYMD(date: Date): string { const yyyy = date.getFullYear(); // 年を取得 const mm = ('0' + (date.getMonth() + 1)).slice(-2); // 月を2桁に整形 const dd = ('0' + date.getDate()).slice(-2); // 日を2桁に整形 return `${yyyy}年${mm}月${dd}日`; } makeDocument(): void { if(this.type === 'invoice') { this.makeInvoice() }else if(this.type === 'deliverySlip') { this.makeDeliverySlip() } } // 請求書を作成する makeInvoice(): void { const line = `商品名:${this.item.name} 価格:${this.item.price} 数量 :${this.item.quantity} 合計金額:${this.item.price * this.item.quantity} ¥n 納品日:${this.formatDateToYMD(this.shipping_date)} ¥n 振込口座:XXX銀行YYY支店 普通口座1234567` this.printToPdf(line) }
  3. 9

  4. 10 //エントリーポイント index.ts type DocumentType = 'invoice' | 'deliverySlip' type

    Item = { name: string price: number quantity: number } const item: Item = { name: '商品A', price: 1000, quantity: 1 } const shipping_date: Date = new Date() const type: DocumentType = 'invoice' function formatDateToYMD(date: Date): string { const yyyy = date.getFullYear(); // 年を取得 const mm = ('0' + (date.getMonth() + 1)).slice(-2); // 月を2桁に整形 const dd = ('0' + date.getDate()).slice(-2); // 日を2桁に整形 return `${yyyy}年${mm}月${dd}日`; } function makeDocument(): void { if (type === 'invoice') { makeInvoice() } else if (type === 'deliverySlip') { makeDeliverySlip() } } // 請求書を作成する function makeInvoice(): void { const line = `商品名:${item.name} 価格:${item.price} 数量:${item.quantity} 合計金額:${item.price * item.quantity} ¥n 納品日:${formatDateToYMD(shipping_date)} ¥n 振込口座:XXX銀行YYY支店 普通口座1234567` printToPdf(line) } // 納品書を作成する function makeDeliverySlip(): void { const line = `商品名:${item.name} 数量:${item.quantity} 納品日 :${shipping_date}` printToPdf(line) } function printToPdf(line: String): void { console.log(line) // PDF出力処理は割愛 } makeDocument() 白河君のクラスは変数の宣言と、関数の宣言がまとめられているだけだから である。 白河君の書いたコードを次のようにエントリーポイントのindex.tsにコピペして も動作する。(this などは書き直す必要あり)
  5. 11 // 納品書を作成する makeDeliverySlip(): void { const line = `商品名:${this.item.name}

    数量:${this.item.quantity} 納品日:${this.shipping_date}` this.printToPdf(line) } printToPdf(line:String): void { console.log(line) // PDF出力処理は割愛 } } export type DocumentType = 'invoice' | 'deliverySlip' export type Item = { name: string price: number quantity: number } //エントリーポイント index.ts import {DocumentMaker, Item} from "./procedure/DocumentMaker"; const item:Item = { name: '商品A', price: 1000, quantity: 1 } const documentMaker = new DocumentMaker('invoice', item, 1) documentMaker.makeDocument() export class DocumentMaker { private readonly type: DocumentType private readonly item: Item private readonly shipping_date: Date = new Date() constructor(type:DocumentType, item:Item) { this.type = type this.item = item } formatDateToYMD(date: Date): string { const yyyy = date.getFullYear(); // 年を取得 const mm = ('0' + (date.getMonth() + 1)).slice(-2); // 月を2桁に整形 const dd = ('0' + date.getDate()).slice(-2); // 日を2桁に整形 return `${yyyy}年${mm}月${dd}日`; } makeDocument(): void { if(this.type === 'invoice') { this.makeInvoice() }else if(this.type === 'deliverySlip') { this.makeDeliverySlip() } } // 請求書を作成する makeInvoice(): void { const line = `商品名:${this.item.name} 価格:${this.item.price} 数量 :${this.item.quantity} 合計金額:${this.item.price * this.item.quantity} ¥n 納品日:${this.formatDateToYMD(this.shipping_date)} ¥n 振込口座:XXX銀行YYY支店 普通口座1234567` this.printToPdf(line) } if文などで分岐していて、今後発注書、見積書などの違うタイプの帳票を 作らなければならなくなった時に、if文が増えていく。
  6. 13

  7. 15 // Document.ts export abstract class Document { protected readonly

    item: Item protected readonly shipping_date: Date = new Date() constructor(item: Item) { this.item = item } formatDateToYMD(date: Date): string { const yyyy = date.getFullYear(); // 年を取得 const mm = ('0' + (date.getMonth() + 1)).slice(-2); // 月を2桁に整形 const dd = ('0' + date.getDate()).slice(-2); // 日を2桁に整形 return `${yyyy}年${mm}月${dd}日`; } abstract print(): void printToPdf(line: String): void { console.log(line) // PDF出力処理は割愛 } } // Invoice.ts import {Document} from "./Document"; export class Invoice extends Document { private static readonly bank_account: string = '振込口座:XXX銀行YYY支店 普 通口座1234567' print(): void{ const line = `商品名:${this.item.name} 価格:${this.item.price} 数量 :${this.item.quantity} 合計金額:${this.item.price * this.item.quantity} ¥n 納品日:${this.formatDateToYMD(this.shipping_date)} ¥n ${Invoice.bank_account}` this.printToPdf(line) } } // DeliverySlip.ts import {Document} from "./Document"; export class DeliverySlip extends Document { print (): void{ const line = `商品名:${this.item.name} 数量:${this.item.quantity} 納品日 :${this.shipping_date}` this.printToPdf(line) } } //エントリーポイント index.ts import {Invoice} from “./oop/Invoice”; import {Invoice} from “./oop/DeliverySlip”; const item:Item = { name: ‘商品A’, price: 1000, quantity: 1 } // 請求書を出したい場合 const invoice:Invoice = new Invoice(item) invoice.print()
  8. 19 ~er で終わるクラス名のプログラムが多いのははあまり よくない設計であることが多い。 (Serverとか汎用的な名詞の~er は除く) ◦◦Processer、◦◦Producer、◦◦Maker、など。 作った人にこのクラスはどういう役割なのか聞くと 「◦◦をする人、みたいな…。」 という擬人化されたクラスとして説明されることが多い。

    その時点で、「◦◦する」クラスなので、手続きがクラスに なっている。 また、人間はいろんなことができるので、そういうクラスに 雑多な処理が詰め込まれてクラスとしての役割がよくわか らない肥大化したクラスになりがち。