オブジェクト指向で実現できる保守性・拡張性保守性・拡張性に優れたシステムを作る(2)(1/3 ページ)

オブジェクト指向開発を行えば、保守性・拡張性が良くなるといわれますが、本当にそうなのでしょうか? 第1回「保守性と拡張性の定義」では、現状のシステム開発の問題点を指摘し、そのうえで、そもそも保守性・拡張性とはどのようなものなのかを説明しました。今回は、拡張性・保守性を高めるための重要な考え方について解説をしていきます。

» 2006年02月08日 12時00分 公開
[野村佳弘,日立ソフトウェアエンジニアリング]

オブジェクト指向による問題の解決

 前回「ソフトウェアにおける保守性と拡張性の定義」は「機能分割」のような従来型の開発手法における問題点を指摘しました。今回はこれらの問題をオブジェクト指向ではどのように考えるのか、また、どうすればこのような問題を解決できるのかについてお話ししたいと思います。

[1] 下流工程における複雑さの解決についての考え方

(1)クラスって何だろう

 最初にクラスとはどのようなものか考えてみましょう。オブジェクト指向では、必ず出てくるクラスとはどのようなものでしょうか?

 Javaなどでクラスというと、メソッドと変数をまとめて定義したものです。そして、利用するときにはインスタンスを生成して利用します。また、変数やメソッドは外部から参照可能かどうかを指定することができます。このようにクラスはまとめて隠し、生成することができます。この、「まとめる」「隠す」「生成する」とは、どのようなことでしょうか?

まとめる

 クラスは操作と属性を持っています。この属性や操作を意味のある塊にまとめたものがクラスです。属性はデータとして保持したい値を一般化したものです。

 属性は変数として定義されます。「田中一郎」「佐藤かおり」というデータを一般化したのが属性「名前」であり、変数「name」で定義されます。

 操作とは操作名と操作の内容であるビジネスロジックを持ちます。操作は操作名により呼び出され、ビジネスロジックを実行し結果を返します。このように操作はサブルーチンに相当します。

 多くの場合、ビジネスロジックはデータや状態[*1]に基づいて結果を返します。データを直接アクセスして利用するのではなく、操作を利用してアクセスします。データとビジネスロジックを1つのクラスにまとめることにより、ビジネスロジックの重複をなくします。そのためにはデータや状態に密接に関連したビジネスロジックを、クラスとしてまとめることが重要です。


[*1]:状態とは、「休止中」や「貸出中」などの状態を表し、一般的にフラグなどで管理されるものです。データとは「田中太郎」や「1000円」など、値そのものを指します


隠す

 操作や属性をクラスの外から隠します。いい換えると、クラスの外に公開できるか指定することができます。基本的に属性は非公開とし、操作によりビジネスロジックなどを利用して属性にアクセスします。この操作や属性を隠ぺいすることをカプセル化といいます。

生成する

 クラスは設計図であり、実体はインスタンスといいます。実際に動作するのは基本的に実体であるインスタンスです。

 インスタンスはクラスから生成されます。同一クラスから、複数のインスタンスが生成できます。たとえ同じクラスからインスタンスを生成しても、それらのインスタンスは別のものです。つまり、それぞれのインスタンスにはデータや状態が別々に存在しています。例えば、会員というクラスはインスタンスとして「会員番号001番の佐藤さん」「会員番号004番の伊東さん」などが存在し、データは「佐藤太郎」「伊東かおり」などの名前があり、「貸出可能」「貸出停止」等の状態が、それぞれのインスタンスには存在します。

 ここで注目していただきたいのが、これらのデータや状態はインスタンスの中に存在することです。そのインスタンスを利用する側は状態やデータを管理する必要はありません。データや状態はクラスの属性としてクラスの内部に定義され隠ぺいされています。これらのデータや状態を知りたい場合は、操作を使って情報を取得します。

ALT 図1 まとめる、隠す、生成する(クリックすると拡大)

 このように、クラスはインスタンスを生成し、インスタンスはそれぞれ自律して存在し、操作を利用して互いに協調しながら動作していきます。

(2)下流工程の問題の解決:プログラム中に重複したロジックや条件文が散在

 それでは、下流工程での複雑さをなくすためのオブジェクト指向での技法についてお話ししたいと思います。この問題の解決については、主に次のような方法があります。

(A)関連する属性や機能をクラスとして1つにまとめることにより、機能の重複を防ぐ

(B)グローバル変数をなくすことにより、プログラムの複雑さをなくす

(C)扱うデータは違うが似たような機能を同一の操作名で利用できるようして、機能を整理しプログラムの複雑さを少なくする

(D)同じ機能を1カ所にまとめて共通に利用できるようにし、機能の重複を防ぐ。ここでの「機能」とは、「ビジネスロジック」と同等の意味と考えてください

 (A)については、クラスの「まとめる」で説明しました。ここでは、(B)と(C)について説明します。(D)については、本連載の中で説明していきます。

1. インスタンス変数によりグローバル変数をなくす

 それでは、最初の(B)について考えてみます。複雑さをもたらしている共通的に参照されるグローバル変数[*2]をなくすにはどうすればよいでしょうか? オブジェクト指向ではクラスにまとめ、隠し、生成する仕組みがあるとお話ししました。インスタンスは内部に状態を保持でき、データを変数に持っています。このクラス内部に持つ変数をインスタンス変数といいます。


[*2]グローバル変数は主に次の用途で利用されます。
・サブルーチン間でのデータや状態の引き継ぎ
・カウンタのようなプログラム全体で参照・更新される変数


 サブルーチンでは、データを内部に持続的に保持することが難しいので、利用する側にグローバル変数としてワークエリアを保持し、状態を管理するフラグや共通的に参照・更新される変数などを定義する必要がありました。

 オブジェクト指向では、インスタンス変数はインスタンスごとにデータや状態を保持します。グローバル変数は、インスタンス変数に置き換えられます。グローバル変数をインスタンス変数としてクラスの内に持たせ、多くのインスタンスを生成しても、個々のインスタンスはインスタンス変数により、データや状態を内部に保持することができ、互いに干渉することがなくなります。

 これにより、グローバル変数によるプログラムの複雑さをなくすことができます。

ALT 図2 オブジェクト指向でのプログラム構造(クリックすると拡大)

2. ポリモーフィズムで機能を整理しプログラムの複雑さを少なくする

 次に、(C)はどのように解決していくのでしょうか。それにはポリモーフィズムを利用して機能を整理していきます。ポリモーフィズムとはどのようなものでしょうか。

ポリモーフィズムとは

 例えば、電話をかける場合、固定電話や携帯電話でも電話の通話に関する操作方法は同じです。たとえ衛星電話が増えたとしても「電話をかける」という操作方法は変わらないので、利用者は操作方法を新たに知る必要はありません。ただ、固定電話や携帯電話の内部の仕組みは異なります。固定電話は電話回線を利用しますし、携帯電話は無線を利用します。操作方法は同じでも機能は違います。

 もし、操作方法が機種ごとに違うと、利用者は電話を利用するときに固定電話か携帯電話か判断し、機種に対応した操作を行わなければなりません。

 このように、ポリモーフィズムとは、利用したい側は同じ操作で利用したいが、操作の機能は違うものを扱う仕組みです。ポリモーフィズムにより、利用する側が種類を判断することなく操作を利用することができます。電話の例でも、操作方法が統一されているので、電話の種類に関係なく通話の操作ができます。このようにすると利用するプログラムから条件式をなくすことができ、単純なプログラム構造にします。Javaのインターフェイスは、ポリモーフィズムを実現しています。

ポリモーフィズムにより似たような機能を整理する

 それでは似たような機能を整理して、単純なプログラム構造にするにはどのように考えればよいのでしょうか。

 似たような機能とは、「固定電話料金を計算する」「携帯電話料金を計算する」など、扱うデータやビジネスロジックは違うが、「電話料金を計算する」という操作は同じように考えることができるような機能です。これにポリモーフィズムを適用します。そうすると複雑さがどのようになくなるのでしょうか。

 図3を例に説明すると、似たような機能(「固定電話料金を計算する」「携帯電話料金を計算する」など)を、データ[*3]の種類(固定電話、携帯電話等)ごとにクラスとしてまとめます。そして、ポリモーフィズム(電話インターフェイス)を定義して、同じように(電話として)扱うことにより、同じ操作名(料金を計算する)で操作することができます。図3の例では、電話インターフェイスには「通信時間を計算する」と「料金を計算する」の操作が定義されています。

 最初に利用したいクラス(固定電話や携帯電話クラス)のインスタンスを生成し、それを利用するプログラム(請求明細)に渡します。後はポリモーフィズムを利用して同じように(電話として)扱うことができ、同じ操作名(「料金を計算する」)で機能(固定電話のインスタンスなら「固定電話料金を計算する機能」であり、携帯電話のインスタンスなら「携帯電話料金を計算する機能」)を利用することができます。これにより、データを判別し該当する機能に振り分ける条件式がなくなります。

 このように、最初に利用したいクラス(固定電話など)を生成すれば、後は電話インターフェースとして共通的に利用することができ、「通信時間を計算する」や「料金を計算する」などの操作を利用するときに、固定電話か携帯電話か等の種別を判断する必要がなくなります。

 そして、新たに「衛星電話」を扱う場合でも、条件式を追加することなく、ポリモーフィズムを実現する電話インターフェイスを実装した「衛星電話」クラスを追加し、衛星電話インスタンスを生成し請求明細インスタンスに渡すだけで、「電話料金を計算する」という操作を利用できます。請求明細クラスには何も変更がありません。変更する個所は、最初に衛星電話インスタンスを生成する個所を1個所変更するだけです。


[*3]:ここで便宜的にデータといっていますが、より正しくは概念です。概念については後半でお話しします。


ALT 図3 ポリモーフィズムによる似たような機能をまとめる(クリックすると拡大)

 このように、ポリモーフィズムを利用することにより、データの種類が違っても扱い方が似ている機能を整理することができます。いままではデータの種類を判断して、データに対応する機能に振り分ける条件式が散在していました(「ソフトウェアにおける保守性と拡張性の定義」を参照)。

 ポリモーフィズムを利用すれば、データの種類に関係なく、同一の操作名でデータに対応する機能を利用できます。データの種類を判別する必要がなくなるので、条件式がなくなりプログラムの構造が簡単になります。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

注目のテーマ