Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

2 • 株式会社オンラインコンサルタント 代表取締役 • プログラミング歴は16年 • 受託開発・自社サービスなど多数の開発 • メインの実績は3000社以上の導入実績がある 「ODIN リアルタイム配送システム」 という配送会社向けのシステム https://delivery-system.com/ @Akiko_Goto999

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

4 ウチの帳票出力システムの開発をお願いします。 ウチでは、納品をすると同時に、納品した製品の請求書と納 品書をPDFにして印刷して顧客に渡しています。 ・請求書のPDFには、商品名、単価×個数、金額、振り込み口 座、出力した日を納品した日として印刷する ・納品書のPDFには、商品名と個数、金額はなし、出力した日 を納品した日として印刷する 顧客

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

6 簡単っす! オブジェクト指向を 使って、設計しま す! 若手プログラマーの白河くん (仮想のキャラクターです) 商品情報をDBから 取得したり、画面を 作ったりするのは 別の人がやります。

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

8 こんな手続き型の プログラムに してもらっちゃ困るよ。 上司 一体どこが 手続き型なんですか!! ちゃんとクラスにしているし、 プロパティも 隠蔽もできています!! 白河君

Slide 9

Slide 9 text

9

Slide 10

Slide 10 text

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 などは書き直す必要あり)

Slide 11

Slide 11 text

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文が増えていく。

Slide 12

Slide 12 text

12 そもそも、クラス名の ”DocumentMaker” って何なの? 上司 そ、それは…。 「帳票を作る人」 というイメージで作りました。 白河君 クラス名の定義があいまい

Slide 13

Slide 13 text

13

Slide 14

Slide 14 text

14 抽象スーパークラス 帳票 (Document) カッコ内はクラス名 クラス 請求書 (Invoice) クラス 納品書 (DeliverySlip)

Slide 15

Slide 15 text

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()

Slide 16

Slide 16 text

16 • if文での分岐がなくなり、将来的に「見積書」や「発注書」 を作ってくれと言われても、作りやすくなった。 • クラスの定義がわかりやすくなった。 • 各クラスに、各クラスの必要なプロパティだけが定義さ れている。

Slide 17

Slide 17 text

17 こんな単純なプログラムを 3つものクラスにするなんて 複雑になっているだけです! プログラミングは わかりやすいのが一番って いつも言ってるじゃないですか! 白河君

Slide 18

Slide 18 text

18 今は単純なプログラムが、 大体だんだん肥大化していく そのために、最初の設計がとても大切

Slide 19

Slide 19 text

19 ~er で終わるクラス名のプログラムが多いのははあまり よくない設計であることが多い。 (Serverとか汎用的な名詞の~er は除く) ○○Processer、○○Producer、○○Maker、など。 作った人にこのクラスはどういう役割なのか聞くと 「○○をする人、みたいな…。」 という擬人化されたクラスとして説明されることが多い。 その時点で、「○○する」クラスなので、手続きがクラスに なっている。 また、人間はいろんなことができるので、そういうクラスに 雑多な処理が詰め込まれてクラスとしての役割がよくわか らない肥大化したクラスになりがち。