3-1プロセス中心とデータ中心
水曜の夜、開発会社から焼き上がり通知の試作プログラムが届きました。ミナトが印刷して作業台に並べると、メロンパン・食パン・クロワッサンの3枚に、ほとんど同じ処理が書かれています。違うのは、パンの名前と焼き上がりの時刻くらいです。そこへ店長が顔を出しました。「来月から、あんバターパンも出したいんだけど」。4つめを足すには、3枚のどれかをまねて書き写し、どこか1か所を直すたびに残りの3枚も同じように直さなければなりません。ミナトが相棒に試作を見せると、こう返ってきました。「共通の部分を1つの部品にして、パンごとの違いだけを分けましょう。その前に、何を軸にして分けるかを決めませんか」
プログラムの分け方を決める出発点は、大きく二つあります。一つは、業務の処理の手順や機能を先に分析し、それを設計の中心に置くプロセス中心アプローチ(POA:Process Oriented Approach)です。焼き上がり通知なら「ボタンが押される→待っている人を探す→知らせを送る」という手順を先に決め、手順ごとに必要なデータを用意します。
もう一つは、業務で扱うデータの構造や、データどうしのつながりを先に分析し、処理はその上に組み立てるデータ中心アプローチ(DOA:Data Oriented Approach)です。こむぎ堂なら、まず「パン」「お客さん」「どのお客さんがどのパンを待っているか」というデータを整理します。業務の手順は店のやり方しだいで変わりますが、扱うデータの中身はそれほど変わらないことが多い、という見方に立っています。
試作プログラムは、プロセス中心で作られた典型でした。パンごとの手順の中に、それぞれのデータが入り込んでいたのです。手順ごとにデータを持つと、同じデータが別々の場所に置かれやすく、手順を変えるとデータの置き方まで変わります。
ここから基本情報!データを図にする
データ中心で設計するときは、管理したい対象とそのつながりを図に描きます。人・物・出来事など、属性を持ち、一つ一つを見分けられる管理の対象を実体(エンティティ)といいます。「パン」と「お客さん」は、どちらも実体です。
実体と実体の結び付きを関連(リレーションシップ)といいます。1人のお客さんは何種類ものパンを待つことができ、1種類のパンは何人ものお客さんに待たれています。この「多対多」のような対応の数も、関連として書き表します。実体を四角、関連を線で結んで描いた図がE-R図です。
基本情報の範囲ここまで
データを先に整理すると、置き方のむだも見えてきます。試作の登録表は、次のような形でした。
| お客さん | 待っているパン | 焼き上がり予定 |
|---|---|---|
| Aさん | メロンパン | 10:30 |
| Bさん | メロンパン | 10:30 |
| Cさん | 食パン | 11:00 |
| Aさん | クロワッサン | 9:00 |
メロンパンの焼き上がりを11:00に変えるには、2行を書き換えなければなりません。片方だけ直し忘れると、同じメロンパンの予定が10:30と11:00の二つになり、どちらが正しいのか分からなくなります。そこで表を「パン(名前・焼き上がり予定)」と「登録(お客さん・待っているパン)」の二つに分け、焼き上がり予定はパンの表にだけ書きます。こうすれば、予定を変えるときに書き換えるのは1行で済みます。
このように、データの重複や、更新したときの食い違いが起きないように表の構造を整えていく手法を正規化といいます。くわしい手順は、データベースの単元で扱います。
ここから基本情報!正規化の根にある原則
「メロンパンは10:30に焼き上がる」という一つの事実は、データの中の一か所にだけ記録する。この原則を一事実一箇所(one fact in one place)と呼びます。正規化は、この原則を表の形で実現する手順だと考えると、目的を見失いません。
基本情報の範囲ここまで
3-2構造化設計
ミナトはノートの1行目に「焼き上がり通知」と書きました。けれど、この1行のままではプログラムになりません。相棒は「その1行を三つくらいに分けて、分けたものをまた分けてみましょう」と言いました。
大きな仕事は、細部からいきなり書き始めるより、上から順に小さな仕事へ分けていくほうが見通しがよくなります。分けた一つ一つを別の部品として作れば、直すときに読む範囲が狭くなり、何人かで手分けもできます。プログラムの設計では、この分け方そのものにいくつかの名前と手順があります。
ここから基本情報!上から分けていく設計
プログラムを機能ごとのモジュール(部品)に分け、モジュールどうしの呼び出しの関係を上下の構造として整理しながら設計する手法を構造化設計といいます。機能を洗い出し、データの流れを明らかにし、機能をまとめて上下の構造に並べ、それぞれの機能を決めて文書にする、という順で進めます。この、機能をモジュールに分ける作業がモジュール分割です。
分けた結果は、上位が下位を含む階層の形になります。そして、全体を大まかに捉えてから、一段ずつ細かい手順へ分解していく進め方を段階的詳細化といいます。ミナトのノートは、次のように詳しくなりました。
- 焼き上がり通知
- 焼き上がりを受け取る(ボタンが押された記録を読む)
- 知らせる相手を選ぶ
- そのパンを登録した人を探す
- 7時台なら知らせない
- 知らせを送る
システムの機能をこのように上から下へ分解し、箱と線で表した図を機能階層図と呼びます。一段下がるごとに、一つの箱が受け持つ仕事は小さく、具体的になります。
基本情報の範囲ここまで
3-3構造化の図法とモジュール仕様
「知らせる相手を選ぶ」の中身を、開発会社の担当の人に文章で伝えようとして、ミナトは3行目で手が止まりました。「もし」と「それ以外」と「全員分」が入り混じって、自分で読み返しても分かりません。相棒は「文章ではなく、図か表にしてみませんか」と提案しました。
処理の手順を、開始・終了・処理・判断などの記号と矢印で表した図を流れ図(フローチャート)といいます。判断の記号から矢印が二つに分かれ、どちらに進むかが一目で追えます。
知らせる相手を選ぶ処理では、登録した人を一人ずつ取り出し、全員について同じ確認をします。このように、条件が満たされている間、同じ処理を何度も実行する構造を繰返し(ループ)といいます。流れ図では、矢印が上へ戻る形で描かれます。
ここから基本情報!三つの基本構造と構造化チャート
どれほど複雑な手順も、三つの基本構造の組み合わせで書けることが知られています。書かれた順に処理を実行する順次、条件によって流れを分ける選択、そして繰返しです。この三つだけで手順を組み立てる書き方を、構造化プログラミングと呼びます。
流れ図では矢印をどこへでも引けるので、三つの構造から外れた行ったり来たりも描けてしまいます。そこで、三つの基本構造だけで処理を図にする手法がまとめて構造化チャートと呼ばれます。代表がNS図(ナッシ・シュナイダーマン図)で、四角い箱を上から積み、選択は箱を左右に割り、繰返しは箱でくるんで表します。矢印を使わないので、構造から外れた流れはそもそも描けません。
モジュールの階層と、各モジュールの入力・処理・出力を組にして図で表す手法がHIPO(Hierarchy plus Input Process Output)です。階層を示す図と、モジュールごとに「何を受け取り、何をして、何を出すか」を書いた図を組み合わせて使います。
手順からではなく、データの構造からプログラムの構造を導く手法もあります。入力データと出力データの構造を順次・選択・繰返しで分析し、その対応からプログラムの構造を決めるのがジャクソン法です。入力データの構造を階層の図(ワーニエ図)で表し、そこから処理の構造を組み立てるのがワーニエ法です。どちらも「データの形がプログラムの形を決める」という立場で、3-1のデータ中心の考え方に近いところにあります。
基本情報の範囲ここまで
図にすると、文章では見落としていた抜けにも気づきます。ミナトが描いた流れ図には、売り切れたときにどうするかを決める判断の記号が、どこにもありませんでした。
ここから基本情報!モジュール仕様の書き方
分けたモジュールの一つ一つについて、どんな機能を持ち、何を受け取り、何を返すのか、外とのやり取り(インタフェース)はどうなっているのかを定めたものをモジュール仕様といいます。中の手順を書き表すには、流れ図やNS図のほかに、表と文の書き方も使います。
条件の組み合わせと、その組ごとに実行する処理を表に整理したものを決定表(デシジョンテーブル)といいます。「在庫が1個以上か」「7時台か」の二つの条件なら、Y(はい)とN(いいえ)の組は2×2=4通りです。
| 規則1 | 規則2 | 規則3 | 規則4 | |
|---|---|---|---|---|
| 在庫が1個以上 | Y | Y | N | N |
| 7時台である | N | Y | N | Y |
| 焼き上がりを知らせる | X | - | - | - |
| 売り切れを知らせる | - | - | X | - |
| 何も送らない | - | X | - | X |
上の段に条件、下の段に処理を並べ、列ごとに「この組ならこの処理(X)」と読みます。4列を埋めると、組み合わせの抜けがないことを表そのものが示してくれます。流れ図で抜けていた「売り切れ」は、規則3として現れました。
特定のプログラム言語の文法にこだわらず、ふだんの言葉に近い形で処理の手順を書いたものを擬似コード(疑似コード)といいます。たとえば「登録した人を一人ずつ取り出す。7時台なら何もしない。そうでなければ知らせを送る。これを全員分繰り返す」のように、字下げで構造を見せながら書きます。
基本情報の範囲ここまで
3-4オブジェクト指向の基本
相棒は、試作の3枚の紙を見比べて言いました。「メロンパンも食パンもクロワッサンも、名前と焼き上がりの時刻と残りの個数を持っていて、焼けたら知らせる。三つとも『パン』という同じ形をしていますね」。ミナトは、3枚の紙を重ねてみました。
データと、そのデータを扱う処理をひとまとまりの「もの」(オブジェクト)として捉え、ものの組み合わせとしてシステムを組み立てる考え方をオブジェクト指向といいます。業務の中にどんなものがあり、それぞれがどう振る舞い、互いにどうつながっているかを分析する工程をOOA(オブジェクト指向分析)と呼びます。こむぎ堂の業務からは「パン」「お客さん」「焼き上がりボタン」といったものが見つかります。
ものが持つデータ(属性)と処理(操作)の型を定めたものをクラスといいます。クラス「パン」は、属性として名前・焼き上がり予定・残りの個数を持ち、操作として「焼き上がりを知らせる」を持ちます。クラスは設計図にあたり、メロンパンも食パンも、この一つの設計図から作られます。
クラスが増えてきたら、関係の深いものをまとめて整理します。通知に関わるクラスを「通知」、会員の情報に関わるクラスを「会員」というように、関連するクラスをひとまとめにする単位をパッケージといいます。名前の重なりを避け、どこに何があるかを探しやすくする役目もあります。
ここから基本情報!クラスとオブジェクトの仕組み
パンには、形も色も香りもあります。しかし通知の仕組みにとって要るのは、名前・焼き上がり予定・残りの個数だけです。対象から目的に要る本質的な性質だけを取り出し、それ以外の細部を省いて捉えることを抽象化といいます。クラスは、抽象化の結果です。
クラスをもとに作られた、具体的な値を持つ一つ一つのオブジェクトをインスタンスといいます。「名前はメロンパン、予定は10:30、残り12個」というオブジェクトが、クラス「パン」のインスタンスです。クラスに定義された操作の手続きはメソッドと呼ばれ、「焼き上がりを知らせる」や「1個売れた」がそれにあたります。
残りの個数を、外のプログラムから自由に書き換えられたらどうなるでしょう。うっかり「-1個」が入れば、通知は混乱します。そこで、データとそれを扱うメソッドを一つにまとめ、データは外から直接触らせず、決めたメソッドを通してだけ扱わせます。これをカプセル化といいます。「1個売れた」のメソッドが0個より下に減らさないようにしておけば、中身をどう作り直しても、外のプログラムは影響を受けません。
オブジェクトどうしは、相手のメソッドを呼び出すことで仕事を頼みます。焼き上がりボタンのオブジェクトがメロンパンのオブジェクトに「焼き上がった」と伝え、受け取った側が自分のメソッドで処理する。この、メッセージを送って処理を依頼する仕組みをメッセージパッシングといいます。
オブジェクト指向の設計には、よく知られた五つの原則があり、頭文字をとってSOLID(ソフトウェア設計原則)と呼ばれます。
- 単一責任の原則(Single Responsibility):一つのクラスが受け持つ役目は一つにする
- 開放閉鎖の原則(Open/Closed):機能の追加には開き、既存のコードの修正には閉じた作りにする
- リスコフの置換原則(Liskov Substitution):親のクラスを使っている所に子のクラスを入れても、正しく動くようにする
- インタフェース分離の原則(Interface Segregation):使わない操作にまで依存させない
- 依存性逆転の原則(Dependency Inversion):具体的な作りではなく、抽象的な取り決めに依存させる
試作のパンごとの3枚は、新しいパンを足すたびに既存の処理を書き換える作りでした。開放閉鎖の原則から最も遠い形です。
基本情報の範囲ここまで
3-5継承と多相性
クラス「パン」の案を見た店長が、腕を組みました。「クロワッサンは予約で取り置く日があるから、予約した人にだけ知らせたいんだよ。夏だけ出すパンは、季節が終わったら知らせなくていいし」。全部のパンが同じ振る舞いをするわけではなかったのです。
共通の部分は一つにまとめたまま、違う部分だけを別に書けると便利です。オブジェクト指向には、そのための仕組みがあります。
ここから基本情報!継承・集約・多相性
あるクラスの属性やメソッドを、別のクラスがそのまま受け継ぐ仕組みを継承(インヘリタンス)といいます。受け継がせる元のクラスをスーパクラス(基底クラス、親クラス)、受け継いで作られるクラスをサブクラス(派生クラス)と呼びます。クラス「パン」をスーパクラスにして、サブクラス「予約パン」を作れば、名前や残りの個数はパンから受け継ぎ、「予約した人にだけ知らせる」部分だけを書き足せば済みます。
上位のクラスから、より具体的な性質を加えた下位のクラスを作り出すことを特化といい、逆に、いくつかのクラスに共通する性質を抜き出して上位のクラスにまとめることを汎化といいます。「パン」から「予約パン」を作るのが特化、「予約パン」と「季節のパン」の共通部分から「パン」をまとめるのが汎化です。
一つのクラスが、二つ以上のスーパクラスから性質を受け継ぐことを多重継承といいます。「予約パン」でもあり「季節のパン」でもあるパンを作るような場合です。二つの親が同じ名前のメソッドを持っていると、どちらを使うのかがあいまいになるため、多重継承を認めない言語もあります。
継承とは別の種類のつながりもあります。「朝の詰め合わせ」という商品は、食パンとクロワッサンを部分として含みます。このように、あるオブジェクトが他のオブジェクトを構成要素として持つ、全体と部分の関係を集約といいます。反対向きに、全体を部分に分けて捉えることを分解といいます。詰め合わせを分解すれば食パンとクロワッサンになり、二つを集約すれば詰め合わせになります。
焼き上がりボタンが押されたとき、通知の仕組みはパンの種類を気にせず、どのパンにも同じ「焼き上がりを知らせる」を送ります。受け取ったのがふつうのパンなら登録した人全員に、予約パンなら予約した人だけに知らせが届きます。同じメソッドの呼び出しに対し、受け取ったオブジェクトの種類によって違う処理が行われる性質を多相性(ポリモーフィズム。ポリモフィズム、多態性とも書きます)といいます。呼び出す側の処理は、パンの種類が増えても書き換えずに済みます。
基本情報の範囲ここまで
3-6ドメイン駆動設計
開発会社との打ち合わせで、話がかみ合わなくなりました。担当の人は「通知対象者」、店長は「待っている人」、ミナトは「登録した人」と呼んでいます。予約したお客さんが「待っている人」に入るのかを確かめて、初めて三人が別々のものを思い浮かべていたと分かりました。
同じものを人によって違う言葉で呼ぶと、決めたはずのことが少しずつずれていきます。業務を知っている人と作る人が、同じ言葉で同じものを指すことは、それ自体が設計の作業です。
ここから基本情報!業務の言葉で設計する
システムが扱う業務の領域をドメインといいます。焼き上がり通知のドメインは「パン屋の焼き上がりと、それを待つお客さん」です。業務の専門家と開発者が一緒に業務を理解し、その理解を設計の中心に置いて作っていく考え方を、ドメイン駆動設計(DDD:Domain-Driven Design)と呼びます。
ドメインに出てくる概念と、その間の関係を整理して表したものがドメインモデルです。「お客さんはパンを待つ。予約したお客さんは、その予約のパンだけを待つ」といった関係を図や言葉で書き、それがそのままクラスの形のもとになります。
業務の専門家と開発者が、同じ意味で使うと決めた共通の言葉をユビキタス言語といいます。こむぎ堂では「待っている人」に決め、打ち合わせでも、設計書でも、プログラムのクラスの名前でも、この言葉を使うことにしました。言葉がそろえば、店長が設計書を読んで誤りに気づくこともできます。
「7時台は知らせない」「残りが0個になったら売り切れを知らせる」のような、業務の決まりそのものを実現する処理をドメインロジック(ビジネスロジック)といいます。画面の表示やデータの保存とは分けて作ることで、画面を作り直しても業務の決まりは変わらずに残ります。
基本情報の範囲ここまで
3-7ソフトウェア要素とプログラムの分割
試作には、焼き上がりのたびに動く通知の処理と、月末に1回動くポイントの集計が、一つのプログラムの中に入っていました。相棒が尋ねます。「月末の夜に集計が重くなったら、その間の通知は待たされませんか」。店長が決めた目標は「ボタンを押してから1分以内に届く」でした。
ここから基本情報!分けるときの基準
プログラムを分けるときは、何を基準に分けるかを先に決めます。その基準の一つが処理能力で、決められた時間の中でどれだけの量の処理をこなせるかを指します。1分以内に知らせるという目標を守るには、時間のかかる月末の集計とは別のプログラムに分け、通知が集計に待たされないようにしておくのが一つの手です。
ソフトウェアの要素を分ける基準には、処理のタイミングの違い、処理の効率の違い、同時に使える資源、入出力装置の特徴などがあります。通知はボタンが押されるたび、集計は月に1回、と処理の周期が違う処理は、別の要素に分ける候補になります。
データのファイルも同じように考えます。毎日読み書きする「いま待っている人」と、たまにしか見ない「去年送った通知の記録」が一つのファイルにあれば、毎回不要なデータまで読むことになります。使い方の違いに応じて一つのファイルを分けることをファイルの分割、反対に、いつも一緒に使う複数のファイルを一つにまとめることをファイルの統合といいます。登録データを1件(1レコード)ずつ読んで処理するレコード処理なのか、まとめて処理するのかも、要素の分け方を左右します。
プログラムを分ける基準として、基本情報のシラバスは処理能力のほかに、分かりやすさ・安全性・開発の生産性・運用性・保守性・再利用性を挙げています。分かりやすさは、一つのプログラムが何をしているかを読んで理解しやすいかどうかの観点です。安全性は、一つのプログラムの誤りや停止がほかへ広がりにくいかどうか、と捉えることができます。
投入した工数や期間に対して、どれだけの成果物を作れるかを開発の生産性(生産性)といいます。分け方によっては、何人かで手分けして同時に作れるようになり、生産性が上がります。ただし、どの基準も一つだけを追うと、ほかの基準を損ねることがあります。細かく分けるほど手分けはしやすくなりますが、プログラムどうしのやり取りが増え、処理能力には不利に働きます。
基本情報の範囲ここまで
3-8モジュールの分割手法
通知のプログラムを分けたあとも、その中の「知らせる相手を選ぶ」部分だけで400行になっていました。どこからどこまでが一つの仕事なのか、開発会社の担当の人にも説明しにくい長さです。
一つのプログラムの中も、さらに小さなモジュールに分けます。分け方には、データがどう流れていくかに着目する方法と、データの構造に着目する方法があり、処理の形に合わせていくつかを組み合わせて使います。
ここから基本情報!分割の手法と、分け方の点検
データの流れを、入力(Source)・変換(Transform)・出力(Sink)の三つの部分に分けてモジュールにする手法をSTS分割といいます。焼き上がり通知なら、ボタンが押された記録を読み取る部分が入力、知らせる相手を選んで文面を作る部分が変換、知らせを送る部分が出力です。データが一本の流れとして入ってきて、加工されて出ていく処理に向いています。
入力されるデータの種類によって、まったく違う処理をする場合もあります。届いた知らせが「焼き上がり」なのか「売り切れ」なのか「予約の取り置き」なのかで処理が分かれるなら、その種類(トランザクション)ごとにモジュールを分けます。これがTR分割(トランザクション分割)です。
「いまが7時台かどうか」の判定は、焼き上がりの処理にも売り切れの処理にも出てきます。このように、いくつものモジュールで共通に使う処理を一つの独立したモジュールとして切り出すことを共通機能分割といいます。切り出したモジュールは、必要な所から呼び出して使うサブルーチンとして作ります。
分けたあとは、モジュールどうしの上下の関係を点検します。上位のモジュールから呼び出されて処理をする下位のモジュールを従属モジュールといいます。あるモジュール自身と、それが直接・間接に呼び出すすべての従属モジュールの範囲をモジュールの制御領域といい、あるモジュールの中で下した判断の結果によって動きが変わるモジュールの範囲をモジュールの影響領域といいます。
望ましいのは、影響領域が制御領域の中に収まっている形です。たとえば「相手を選ぶ」モジュールが7時台かどうかを判定し、その結果で、兄弟にあたる「知らせを送る」モジュールの動きまで変えようとすると、判断の結果を上位を経由して横へ回すことになります。この場合は、判定を両者の上位のモジュールに移すと、判断の影響が自分の配下だけで閉じます。
分けたモジュールの数や一つの大きさが、多すぎず少なすぎないかを見る基準が分割量です。細かく分けすぎると呼び出しとやり取りが増え、粗すぎると一つのモジュールが読みにくくなります。こうした点検の結果をもとに、分け方を見直して組み直すことをモジュール再分割といいます。400行の「相手を選ぶ」は、再分割で「登録した人を探す」「時刻で除く」「予約で絞る」の三つになりました。
基本情報の範囲ここまで
3-9モジュールの独立性
試作には「便利処理」という名前のモジュールがありました。中を開くと、時刻の判定、通知の文面づくり、ポイントの計算がまとめて入っていて、呼び出すときに渡す番号で、どれを動かすかを切り替えています。ポイントの計算を直したら、なぜか通知の文面が崩れました。
一つを直したら別の所が壊れる。それは、モジュールどうしが見えない所でつながっているしるしです。分けたモジュールがどれだけ独立しているかを測る物差しがあります。
ここから基本情報!強度と結合度
モジュールがほかのモジュールからどれだけ切り離されているかをモジュール独立性といいます。独立性は二つの物差しで評価します。一つはモジュールの中のまとまりの強さ、もう一つはモジュールとモジュールのつながりの強さです。
モジュールの内部の機能が、どれだけ一つの目的に向かってまとまっているかを結束性(モジュールの結束性。強度ともいいます)と呼びます。強いほど独立性が高く、望ましい形です。強いものから順に、次の七つに分けられます。
| 強度(上ほど強い) | まとめ方 | こむぎ堂の例 |
|---|---|---|
| 機能的強度 | 一つの機能だけを持つ | 「7時台か」だけを判定する |
| 情報的強度 | 同じデータ構造を扱う複数の機能を、それぞれ入口を分けて一つにまとめる | 登録データの追加・削除・検索を一つにまとめる |
| 連絡的強度 | 同じデータを受け渡しながら順に実行する機能をまとめる | 登録を読み、その結果で文面を作る |
| 手順的強度 | 決まった順に実行する機能をまとめる(データの関係はない) | 開店時の手順を順に並べる |
| 時間的強度 | 同じ時期に実行する機能をまとめる | 起動時の初期化をまとめる |
| 論理的強度 | 似た機能をまとめ、引数で実行する機能を選ぶ | 番号で「焼き上がり」「売り切れ」の文面を切り替える |
| 暗合的強度 | 関係のない機能を、たまたま一つにまとめる | 試作の「便利処理」 |
情報的強度は、同じデータを扱う操作を一か所に集める形で、3-4のカプセル化をしたクラスとよく似ています。「便利処理」は、時刻の判定とポイントの計算という関係のない機能が同居した、最も弱い暗合的強度でした。
もう一つの物差しが、モジュールとモジュールがどれだけ依存し合っているかを表す結合度です。こちらは弱いほど独立性が高く、望ましい形です。強いものから順に、次の六つに分けられます。
| 結合度(上ほど強い) | つながり方 | こむぎ堂の例 |
|---|---|---|
| 内容結合 | 相手のモジュールの内部のデータや処理に直接入り込む | 通知のモジュールが、在庫のモジュールの内部の数を直接書き換える |
| 共通結合 | 複数のモジュールが、共通の大域的なデータ領域をまとめて読み書きする | 「今日の店の状態」という大きな共通領域を全員で書き換える |
| 外部結合 | 外部に公開した単一のデータを共有する | 「現在時刻」の一項目だけを共有する |
| 制御結合 | 相手の処理の流れを指示する制御情報(フラグなど)を渡す | 「1なら焼き上がり、2なら売り切れ」と番号を渡す |
| スタンプ結合 | データ構造をまるごと渡し、相手はその一部だけを使う | お客さんの情報を丸ごと渡し、名前だけ使う |
| データ結合 | 必要な値だけを引数で渡す | パンの名前と時刻だけを渡す |
論理的強度のモジュールを呼び出すには「どの機能か」を表す番号を渡すことになるので、制御結合が生まれます。中のまとまりを強くすると、外とのつながりも細くなるのです。「便利処理」は、時刻の判定、文面づくり、ポイントの計算の三つのモジュールに分け、必要な値だけを渡すデータ結合に改めました。
基本情報の範囲ここまで
3-10部品化と再利用
分け直した「7時台か」の判定は、ネットの予約システムの「早朝の受け取り不可」にもそのまま使えそうでした。開発会社の担当の人によると、知らせをスマートフォンへ送る処理も、別の店のために作ったものが手元にあるそうです。
一度作ってよく確かめた処理を、別の場面でも使えれば、作る手間も、誤りが入る機会も減らせます。そのためには、どこへ持っていっても使える形にしておく必要があります。
ここから基本情報!部品として使い回す
使い回すことを前提に、独立した機能の単位として用意したプログラムの構成要素を部品といいます。プログラムの機能を、そうした独立した部品として切り出すことを部品化といいます。部品にするには、前の節の独立性が欠かせません。ほかのモジュールの内部に入り込んでいる処理は、そこから切り離して持ち出せないからです。
すでにある部品やプログラムを、別のシステムや用途で、そのまま、あるいは少し直して使うことを再利用といいます。別の店で使われてきた送信の部品なら、作る手間が省けるうえ、使われてきた分だけ誤りが見つかって直されている、という期待も持てます。
独立した部品(コンポーネント)を組み合わせてソフトウェアを作る考え方、またそのための部品群をコンポーネントウェアと呼びます。部品の中身を知らなくても、入力と出力の約束(インタフェース)さえ分かれば組み込めることが前提です。一方で、どこでも使えるように汎用に作りすぎると、部品が大きく重くなります。何をどこまで部品にするかも、設計で決めることの一つです。
基本情報の範囲ここまで
3-11アーキテクチャパターンとデザインパターン
試作では、お客さんの画面のボタンの色を変えるだけで、通知の決まりを書いた部分まで直す必要がありました。画面を作る処理と、「7時台は送らない」という決まりが、同じ所に混ざっていたからです。相棒は「こういう悩みには、昔から決まった解き方があります」と言いました。
設計で出会う悩みの多くは、ほかの誰かもすでに出会っています。よく使われる解き方には名前が付き、型として受け継がれています。
ここから基本情報!構造の型と設計の型
ソフトウェア全体の構造の型をアーキテクチャパターンと呼びます。代表がMVCモデルで、処理をデータと業務の決まりを扱うモデル(Model)、画面の表示を受け持つビュー(View)、利用者の操作を受け取って両者を制御するコントローラ(Controller)の三つに分けます。ボタンの色を変えるのはビューの中だけで済み、通知の決まりはモデルに残ります。
この考え方をさらに進めたものにクリーンアーキテクチャがあります。業務の決まり(3-6のドメインロジック)を中心の層に置き、画面・データベース・外部のサービスといった技術の部分を外側の層に置きます。そして、外側が内側に依存し、内側は外側を知らない、という依存の向きを守ります。送信のサービスを別の会社のものに替えても、「7時台は送らない」の決まりは書き換えずに済みます。
もっと小さな単位、つまりクラスやオブジェクトの組み立て方でよく出会う問題と、その解き方を、再利用できる形にまとめたものをデザインパターンといいます。広く知られているのは、4人の著者が1994年の書籍でまとめた23のパターンで、目的によって三つに分類されています。
| 分類 | 何のための型か | パターンの例 |
|---|---|---|
| 生成パターン(生成に関するパターン) | オブジェクトの作り方を柔軟にする | Singleton(インスタンスを一つに限る)、Factory Method |
| 構造パターン(構造に関するパターン) | クラスやオブジェクトを組み合わせて大きな構造を作る | Adapter(合わない口をつなぐ)、Composite |
| 振る舞いパターン(振る舞いに関するパターン) | オブジェクトの間の責任の分け方や、やり取りの仕方を決める | Observer(変化を登録した相手に知らせる)、Strategy |
焼き上がり通知そのものが、振る舞いパターンの Observer の形をしています。パンのオブジェクトは、自分を待っている人の一覧だけを持ち、焼き上がったら一覧の全員に知らせます。待っている人が何人増えても、パンの側は書き換えずに済みます。パターンの名前を知っていれば、開発会社の担当の人とも「ここは Observer で」の一言で話が通じます。
基本情報の範囲ここまで
3-12レビュー
金曜の閉店後、直した設計書を開発会社の担当の人と店長と一緒に読む時間を取りました。ミナトは、自分で描いた図に自分では気づけない誤りがあるかもしれない、と少し緊張しています。
設計の誤りは、プログラムを書いた後よりも、書く前に見つけたほうが直す手間が小さく済みます。作った人とは別の目で成果物を読み、問題を早く見つける作業をレビューと呼び、目的や進め方によっていくつかの種類があります。
ここから基本情報!レビューの種類と役割
設計の内容が要件を満たしているか、無理や漏れがないかを、関係者が集まって確かめる検討をデザインレビュー(設計レビュー)といいます。金曜の集まりは、焼き上がり通知のデザインレビューです。
同じ立場の技術者どうしが互いの成果物を確かめ合い、誤りや改善点を見つけるレビューをまとめてピアレビューと呼びます。ピアレビューの代表的な進め方が二つあります。
作成者自身が説明役になり、参加者に成果物を順に説明しながら、一緒に問題を見つけていくのがウォークスルーです。形式は比較的ゆるやかで、気軽に開けます。これに対し、あらかじめ決めた手順とチェックリストに沿い、参加者の役割を分けて欠陥を探す、形式の整ったレビューがインスペクションです。インスペクションでは、作成者ではないモデレーターが会議の計画や進行を受け持ち、議論が脱線しないように取り仕切ります。
レビューは、読む対象が文書になっていて初めて成り立ちます。流れ図・決定表・状態遷移図など、処理の手順を図や表で誰が読んでも同じように分かる形に書き表す技法を文書化手法といいます。金曜のレビューで店長が誤りに気づけたのも、決定表の「売り切れ」の列があったからでした。
基本情報の範囲ここまで
翌週の月曜、開発会社から直したプログラムが届きました。パンごとの3枚の紙は、クラス「パン」の1枚にまとまっています。ミナトが、あんバターパンの名前と焼き上がりの時刻と個数を1行書き足すと、テスト用のスマートフォンに「あんバターパン 焼き上がりました」と知らせが届きました。店長は「1行でいいの」と画面をのぞき込みます。
試作で苦しんだのは、プログラムを書く腕前ではなく、分け方でした。パンごとに処理を書き写した形では、新しいパンが増えるほど直す場所も増えます。共通の部分を一つのクラスにまとめ、パンごとに違うのは名前や時刻といったデータだけにしたことで、足す作業は1行になりました。データを先に整理すること、上から順に分けること、中のまとまりを強くして外とのつながりを細くすること。この章の手法は、どれも「次に直すときに、どこを読めば済むか」を小さくするためのものです。
ただ、ミナトには気になることが一つ残っています。設計は整っても、書いたプログラムが本当にそのとおり動くかは、まだ誰も確かめていません。残りが0個のときにも、きちんと「焼き上がりました」を出さずにいられるでしょうか。