Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

3 • プログラミングを始めて3年以上 • オブジェクト指向でプログラミングをしている • クラス設計などを担当するようになったが、設計 とはどうすればよいのかわからない • なんだか設計が後一歩と感じる

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

5 プログラムに登場する 「クラス(Class)」 をどのように設計・構造化するかを考える作業 オブジェクト指向プログラミング(OOP)に おいて非常に重要なステップ

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

7 コールセンターの通話記録システムの開発をお願いします。 コールセンターの社員が顧客のサポートのために電話をしま す。 ・電話をした回数を数えたい ・社員の誰が、どの顧客と何回、どのぐらいの時間、何を話し たか、顧客の問題は解決したかという記録を取りたい ・電話をした顧客が電話を取らなかった場合も、不在というこ とで不在の回数をカウントしたい ・前の通話からどれぐらいの時間が経ってから電話している のかも知りたい ・顧客が電話してきたのか、こちらから電話したのかも分けた い 顧客

Slide 8

Slide 8 text

8 よし! UML書こう ! 若手プログラマーの白河くん (仮想のキャラクターです) UML(ユーモデル、Unified Modeling Language/ 統一モデリング言語)とは、 システムやソフトウェアの設計を「図」で表現する ための標準的な表記法

Slide 9

Slide 9 text

9

Slide 10

Slide 10 text

10 コードはTypeScriptで書いていきます。 export class Employee { constructor( public id: number, public name: string, public callRecords: CallRecord[] = [] ) {} } export class Customer { constructor( public id: number, public name: string, public callRecords: CallRecord[] = [] ) {} } export class CallRecord { constructor( public id: number, public durationMinutes: number, public timestamp: Date, public direction: Direction, public status: CallStatus, public topic: string, // 通話内容 public resolved: boolean, public employee: Employee, public customer: Customer, public previousCallIntervalMinutes: number = 0 // 計算してセットす る ) { employee.callRecords.push(this); customer.callRecords.push(this); } } // enums.ts export enum Direction { INBOUND = "INBOUND", OUTBOUND = "OUTBOUND", } export enum CallStatus { COMPLETED = "COMPLETED", NO_ANSWER = "NO_ANSWER", } //エントリーポイント index.ts const emp = new Employee(1, "山田 太郎"); const cust = new Customer(1, "佐藤 花子"); // 1回目の通話(発信) const call1 = new CallRecord( 1, 10, new Date("2025-04-01T10:00:00"), Direction.OUTBOUND, CallStatus.COMPLETED, "パスワードリセット", true, emp, cust, 0 );

Slide 11

Slide 11 text

11 このままだと、2つ問題点がありそうだね。 ①通話が不通だった場合に、通話内容 がない場合があるけれども、通話内容 は不通ではない場合に必ず入力させる ようにバリデーションしたいよね。 ②顧客の要求が増えたときにCallRecord にプロパティがどんどん増えていくよう に予想できるよ。 上司 ①についてはコンストラクタで不通 ではない場合に、通話内容が空で はないようにチェックすればいい じゃないですか! ②については、今困ってないので いいじゃないですか!! 白河君

Slide 12

Slide 12 text

12

Slide 13

Slide 13 text

13 よし! 図を書いてみよう ! 若手プログラマーの発田くん (仮想のキャラクターです)

Slide 14

Slide 14 text

14 この時、図や絵は適当でもよい。細かいことも書かなく てよい。書くことがまず大事。 ポイントは、なるべく紙やホワイトボードのようなアナロ グのモノを使うこと。 コールセンター の人 顧客 電話する 記録 発田くん

Slide 15

Slide 15 text

15 コールセンター の人 顧客 電話する 記録 「コールセンターの人」「顧客」はクラス、電 話の内容は「通話記録」というクラスにでき そう。しかし、「コールセンターの人」「顧 客」以外がなんでもかんでも「通話記録」に 入ることになるな? Employee CallRecord Customer 緑の斜体字が書 きこんだクラス名 発田くん

Slide 16

Slide 16 text

16 コールセン ターの人 顧客 電話する 記録 Employee CallRecord Customer 発田くん 若手プログラマー の中田さん なんか足りな い気がする 足りない気がするという 意味はわからないけど、 どうして「電話する」の横 にはクラス名がないの? !!

Slide 17

Slide 17 text

17 コールセンター の人 顧客 電話する 記録 Employee CallRecord Customer 発田くん 若手プログラマー の中田さん そっか!「電話する」を クラスにしたらいいん だ!! Call しっくりくるね。

Slide 18

Slide 18 text

18 発田くん

Slide 19

Slide 19 text

19 コードはTypeScriptで書いていきます。 export class Employee { constructor( public id: number, public name: string, public calls: Call[] = [] ) {} } export class Customer { constructor( public id: number, public name: string, public calls: Call[] = [] ) {} } export class CallRecord { constructor( public id: number, public topic: string, public resolved: boolean, public previousCallIntervalMinutes: number ) {} } export enum Direction { INBOUND = "INBOUND", OUTBOUND = "OUTBOUND", } export enum CallStatus { COMPLETED = "COMPLETED", NO_ANSWER = "NO_ANSWER", } // 左のコードの続き export class Call { public record?: CallRecord; constructor( public id: number, public timestamp: Date, public durationMinutes: number, public direction: Direction, public status: CallStatus, public employee: Employee, public customer: Customer ) { this.employee.calls.push(this); this.customer.calls.push(this); } setRecord(record: CallRecord) { this.record = record; } } //エントリーポイント index.ts const emp = new Employee(1, "山田 太郎"); const cust = new Customer(1, "佐藤 花子"); const call = new Call( 1, new Date(), 15, Direction.OUTBOUND, CallStatus.COMPLETED, emp, cust ); const record = new CallRecord(1, "プリンターの紙詰まり", true, 120); call.setRecord(record);

Slide 20

Slide 20 text

20 ①通話が不通だった場合は通話内容が ない場合がある、という状態をモデルで 表現できているのはよいね!通話内容 は不通ではない場合に必ず入力させる ようにバリデーションもしやすいね。 ②顧客の要求が増えた時でもCallか CallRecordにプロパティを増やせるので 余裕があるね! 上司 ありがとうございます! 発田君

Slide 21

Slide 21 text

21 図や絵にする 人に相談する UMLにする

Slide 22

Slide 22 text

22 どうして絵や図を書いたほうがいん ですか?書くのが面倒です。 それに、紙とかホワイトボードとか、 非効率に思います。今どき、もっとデ ジタルなツールの方が、コピーや シェアも簡単なのでデジタルなツー ルがいいですよ! 白河君

Slide 23

Slide 23 text

23 絵や図を書くことが面倒 • 後でクラス設計、つまりプログラムやDBの構造など変更するほ うがずっと工数がかかることになる • 自分の思考を図や絵にすることは、プログラマーでなくても仕事 の重要なプロセスの一つとして根付かせる 後で直すのはもっと面倒

Slide 24

Slide 24 text

24 紙やホワイトボードなんて非効率 • ホワイトボードは広いことがメリット。アイデアの追加が容易。 • 紙のノートは自由に図や矢印、枠線などが書けるので、柔軟な 思考を促す 書く媒体によって思考が変わる ことは実証されている

Slide 25

Slide 25 text

25

Slide 26

Slide 26 text

26 コールセンター の人 顧客 電話する 記録 Employee CallRecord Customer Call この図で、「電話する」をクラスにすることでブレークスルーがありま した。 名詞のもの(ここでは「コールセンターの人」「顧客」「記録」に関して クラスにしようという発想は生まれやすいですが、「電話する」などの 動作をクラスにすることは発想として生まれづらいことが多いです。 しかし、動作をクラスにして扱うとうまくいくケースが時々あります。