基本情報技術者 2014年 秋期 午後 問05
共通ライブラリのオブジェクト指向設計に関する次の記述を読んで、設問1,2に答えよ。
システムインテグレータのT社は、自社で開発するソフトウェアの品質の安定化、開発の生産性の向上などを目的として、オブジェクト指向を用いた共通ライブラリの設計に取り組んでいる。
これまでT社では、システム開発において社員検索機能を数多く開発してきた。この機能は、組織情報から特定の社員を探すために使用される。図1に示すように、左側に階層表示されている組織の一つを選択すると、右側にその組織に属する社員及び下位の組織の情報が表示されるので、目的とする社員が見つかるまで下位の組織を選択して検索する。図1は、左側で金融システム部を選択し、右側に金融システム部に属する社員及び下位の組織を表示している例である。
T社では、この組織の階層構造情報(以下、組織階層という)を、デスクトップアプリケーションソフトウェア、Webページなどのユーザインタフェースに出力するシステムを開発してきた。

組織階層の扱いは、どの開発プロジェクトにおいても類似するものが多い。そこで、組織階層を扱う機能をユーザインタフェースから切り離して、様々な開発プロジェクトで再利用できるように、共通ライブラリとして設計することにした。共通ライブラリは、オブジェクト指向で設計し、UMLのクラス図で表現する。ここで、共通ライブラリの設計に当たり、共通ライブラリを利用する側のソフトウェアを一律にクライアントと呼ぶことにする。
まず、分析のためのクラス図を作成した。
〔分析のためのクラス図の説明〕
組織階層において、ある組織が親の組織(以下、親組織という)をもつ場合、親組織は必ず一つであり、子の組織(以下、子組織という)をもつ場合、子組織の数に制限はない。また、親組織をもたない組織、子組織をもたない組織、親組織も子組織ももたない組織もある。社員は必ず一つの組織だけに属する。
(1) 組織階層は木構造で管理する。
(2) クライアントが組織階層を参照する際、組織と社員を共通に扱えるようにするために、どちらも組織エントリとして管理する。
図2は、分析のためのクラス図である。
次に、組織エントリを管理するクラスの、設計のためのクラス図を作成した。
〔設計のためのクラス図の説明〕
組織は自身に属する組織エントリのリスト(以下、組織エントリリストという)を、属性として保持する。
この組織エントリリストを設計するためのクラス図を図3に、組織エントリリストに定義する属性と操作の説明を表1に示す。以下、操作をメソッドという。
(1) 組織エントリリストは、組織エントリをサイズが可変の配列で管理する。
(2) クライアントは、組織エントリリストに表1のメソッドでアクセス(組織エントリの追加や削除、取出し、走査など)する。


設問1:
問題文を見る 図2の分析のためのクラス図の中のに入れる正しい答えを、解答群の中から選べ。
a, bに関する解答群
ア:親組織
イ:クライアント
ウ:子組織
エ:社員
オ:組織
カ:組織エントリ
キ:組織階層
c, dに関する解答群
ア:0
イ:0..1
ウ:0..*
エ:1
オ:1..*
模範解答
a:カ、b:オ、c:ウ、d:イ
解説
解答の導き方
まず、aとbが何を表すクラスなのかを考えます。クラス図は、ソフトウェアで扱うものと、その間の関係を表す図です。図2では、社員と組織が共通のクラスから継承する形になっています。継承とは、共通する性質を上位のクラスにまとめ、下位のクラスがそれを引き継ぐ仕組みです。
本文には、組織と社員を「どちらも組織エントリとして管理する」とあります。したがって、社員と組織に共通する上位のクラスであるaには「組織エントリ」が入ります。
次に、組織エントリをまとめて保持する側のクラスを考えます。本文には、組織が自身に属する組織エントリのリストを保持するとあります。また、組織エントリには、子組織だけでなく社員も含まれます。図2でそのリストを持つ側のクラスは「組織」なので、bには「組織」が入ります。
続いて、cとdは、組織と組織エントリの関係における個数を表します。クラス図の多重度は、あるインスタンスに対して、関連する相手が何個あり得るかを示します。
一つの組織が持つ組織エントリの数を考えると、子組織や社員がいない場合もあれば、複数いる場合もあります。本文でも、子組織の数に制限はないと説明されています。そのため、組織一つに対する組織エントリの数は「0..」となり、cには「0..」が入ります。
反対に、一つの組織エントリが属する組織の数を考えます。社員は必ず一つの組織に属しますが、親組織をもたない組織もあります。組織と社員をまとめた「組織エントリ」全体で考えると、属する組織は「ない場合」または「一つの場合」です。したがって、dには「0..1」が入ります。
誤りやすいポイント
- cとdの向きを取り違えないようにします。 cは一つの組織に属する組織エントリの数、dは一つの組織エントリが属する組織の数です。
- 社員は必ず一つの組織に属しますが、組織エントリには組織も含まれます。親組織をもたない組織があるため、dは「1」ではなく「0..1」です。
- 組織が保持するのは子組織だけではありません。組織エントリには社員も含まれるため、組織と社員をまとめて数えます。
- 組織に属する組織エントリが必ず一つ以上あるとは限りません。0個の場合もあるので、cは「1..*」ではありません。
FAQ
Q: 社員は必ず一つの組織に属するのに、なぜdは「0..1」なのですか?
A: dは社員だけでなく、組織エントリ全体に対する多重度だからです。社員の場合は一つですが、親組織をもたない組織の場合はゼロです。
A: dは社員だけでなく、組織エントリ全体に対する多重度だからです。社員の場合は一つですが、親組織をもたない組織の場合はゼロです。
Q: cの「組織エントリ」には何が含まれますか?
A: 子組織と社員の両方が含まれます。本文では、組織と社員を共通に扱うため、どちらも組織エントリとして管理しています。
A: 子組織と社員の両方が含まれます。本文では、組織と社員を共通に扱うため、どちらも組織エントリとして管理しています。
Q: 図2の菱形は何を表しますか?
A: 集約を表します。ここでは、組織が組織エントリのリストを持つという、全体と部分の関係を示しています。
A: 集約を表します。ここでは、組織が組織エントリのリストを持つという、全体と部分の関係を示しています。
関連キーワード: UML、クラス図、継承、多重度、集約、オブジェクト指向設計
設問2:
問題文を見る 次の記述中のに入れる適切な答えを、解答群の中から選べ。
共通ライブラリの社内レビューを実施したところ、組織エントリリストのクラス設計に対して次の指摘が挙がった。
〔指摘の内容〕
組織エントリリストから組織エントリを取り出すとき、クライアントが要素番号を指定する必要がある。全ての組織エントリを取り出すときや、特定条件を満たす組織エントリだけを取り出したいときなど、クライアント側の実装依存が大きくなる。図4にクライアントの走査の例を示す。

共通ライブラリは、組織エントリリストの内部構造をクライアントに意識させず、組織エントリリストの走査を拡張できるように改善すべきである。
この指摘を受け、組織エントリリストのクラス設計を次の設計方針に従って見直すことにした。
〔設計方針〕
(1) 組織エントリリストは、より汎用的にするために、任意のオブジェクトを管理できるようにする。
(2) リストを走査する処理は、別のクラス(以下、イテレータという)に与える。リストでは、このイテレータを生成してeをイテレータに登録することでイテレータがリストを走査できるようにする。
(3) 共通ライブラリで提供するイテレータは、先頭から順に全ての要素を走査するメソッドを提供する。
(4) アクセス方法の統一を目的として、必要なメソッドをインタフェースとして定義する。クライアントは、必ずこのインタフェースを用いてリストを走査することとする。
(5) クライアントは、共通ライブラリで提供するメソッドと異なる走査をしたい場合は、fする。
図5は見直し後の設計のためのクラス図、表2は図5のクラス図の説明である。


eに関する解答群
ア:クライアント
イ:属性
ウ:メソッド
エ:リスト自身
fに関する解答群
ア:BaseListとBaseIteratorを継承して処理をオーバライド
イ:BaseListとBaseIteratorを継承してメソッドを追加
ウ:BaseListとBaseIteratorを実装して処理をオーバライド
エ:BaseListとBaseIteratorを実装してメソッドを追加
オ:ListとIteratorを継承して処理をオーバライド
カ:ListとIteratorを継承してメソッドを追加
キ:ListとIteratorを実装して処理をオーバライド
ク:ListとIteratorを実装してメソッドを追加
模範解答
e:エ、f:ア
解説
解答の導き方
この設問では、リストを調べる処理を別のクラスに任せる設計について、イテレータに渡すものと、独自の走査を追加する方法を考えます。イテレータとは、リストの要素を順番に取り出すための仕組みです。
eについて
- 設計方針では、リストがイテレータを生成するとあります。図5のBaseIteratorには、走査対象のリストを保持する属性listがあります。
- イテレータがリストを走査するには、どのリストを調べるのかを知っている必要があります。そのため、リストを生成したイテレータに渡します。
- イテレータを生成するのはリストです。したがって、渡すのは「リスト自身」です。これによって、イテレータは属性listを通じて走査対象のリストを参照できます。
fについて
- 設計方針では、クライアントはインタフェースを通じてリストを走査するとあります。インタフェースとは、利用できるメソッドを定めた約束事です。図5では、ListとIteratorがインタフェースです。
- 共通ライブラリのBaseListとBaseIteratorは、その約束事に沿って実際の処理を提供するクラスです。クラスとは、データや処理をまとめた設計図です。
- 共通ライブラリの標準の走査とは異なる処理をしたい場合は、標準のクラスを基に新しいクラスを作り、必要な処理を変更します。このように、元のクラスの機能を引き継ぐことを継承、引き継いだメソッドの処理を置き換えることをオーバライドといいます。
- 例えば、後ろから走査したり、組織だけを走査したりするには、BaseIteratorの走査処理をオーバライドします。また、そのイテレータを生成するために、BaseListのcreateIteratorも必要に応じてオーバライドします。
- 一方、ListとIteratorは処理の実装を持たないインタフェースです。これらを実装するだけでは、BaseListやBaseIteratorにある標準の処理を引き継げません。既存の処理を活用して変更するので、「BaseListとBaseIteratorを継承して処理をオーバライド」が適切です。
誤りやすいポイント
- イテレータに渡すのは、走査を依頼するクライアントではありません。イテレータが調べる対象である、リスト自身です。
- 属性listは、走査対象のリストをイテレータが保持するためのものです。eでは、何をイテレータに渡すかを考えます。
- 独自の走査を実現するには、メソッドを追加するだけでは足りません。標準の走査処理と異なる動きをさせるため、既存の処理をオーバライドする必要があります。
- ListとIteratorはインタフェースなので、BaseListとBaseIteratorのように処理を引き継ぐためのクラスとは役割が異なります。
FAQ
Q: イテレータを使うと、クライアントは何をしなくてよくなりますか?
A: 配列の要素番号を使って、リストの中身を直接たどる必要がなくなります。リスト内部の管理方法を意識せずに、イテレータを通じて要素を調べられます。
A: 配列の要素番号を使って、リストの中身を直接たどる必要がなくなります。リスト内部の管理方法を意識せずに、イテレータを通じて要素を調べられます。
Q: 独自の走査を加えるとき、クライアントは独自のクラスを直接使うのですか?
A: いいえ。設計方針では、クライアントはインタフェースを用いて走査します。独自のクラスもListやIteratorの約束事に沿って作ることで、同じ使い方を保てます。
A: いいえ。設計方針では、クライアントはインタフェースを用いて走査します。独自のクラスもListやIteratorの約束事に沿って作ることで、同じ使い方を保てます。
Q: イテレータのfirstとcurrentItemは何をしますか?
A: firstは走査位置を先頭に戻します。currentItemは現在の位置の要素を返し、次の要素へ進みます。isDoneは、走査する要素が残っているかを確認します。
A: firstは走査位置を先頭に戻します。currentItemは現在の位置の要素を返し、次の要素へ進みます。isDoneは、走査する要素が残っているかを確認します。
関連キーワード: イテレータ、インタフェース、継承、オーバライド、クラス設計




