前回の隠蔽化の話を受け、実際のプログラムの例を課題に説明。 複素数クラスを(実部,虚部)で実装した後に、(絶対値,偏角)に直したら…
基本プログラム(実部と虚部)
複素数を扱うクラスを作るのであれば、基本的には以下の様なコードとなるだろう。 複素数どうしの簡単な加算・乗算を記載する。
class Complex {
private:
double re , im ;
public:
Complex( double x , double y ) {
re = x ;
im = y ;
}
// 上記コンストラクタは、以下のようにも書ける。
// Complex( double x , double y )
// : re( x ) , im( y )
// { メンバ以外の初期化... }
void print() {
printf( "%lf+j%lf¥n" , re , im ) ;
}
void add( Complex &z ) {
re = re + z.re ;
im = im + z.im ;
}
void mul( Complex &z ) {
double x = re * z.re - im * z.im ;
double y = re * z.im + im * z.re ;
re = x ;
im = y ;
}
} ;
int main() {
Complex a( 1 , 2 ) ;
Complex b( 2 , 3 ) ;
a.add( b ) ;
a.print() ;
a.mul( b ) ;
a.print() ;
return 0 ;
}
Complexクラス内部をリファクタリング
しかし、前述プログラムでは、mul()メソッドは、add()メソッドよりは、 複雑なものとなっている。 しかし、複素数の乗算は、(絶対値と偏角)を用いれば、絶対値の乗算・偏角の加算で 処理は簡単に記述できる。そこで、クラス内部を乗算と偏角で処理をするように変更してみる。
class Complex {
private:
double r , th ;
public:
Complex( double x , double y ) {
r = sqrt( x*x + y*y ) ;
th = atan2( y , x ) ; // atan2は象限を考慮してくれる
}
void print() {
printf( "%lf ∠ %lf¥n" , r , th / 3.141592 * 180.0 ) ;
}
void add( Complex &z ) {
// ここは面倒な式になっちゃう
}
void mul( Complex &z ) {
r = r * z.r ;
th = th + z.th ;
}
} ;
int main() {
Complex a( 1 , 2 ) ;
Complex b( 2 , 3 ) ;
a.add( b ) ;
a.print() ;
a.mul( b ) ;
a.print() ;
return 0 ;
}
ここで重要なポイントは、2つめの絶対値∠偏角のプログラムの呼び出し側 main() は、 1つめのプログラムとまるっきり同じである。
このように、オブジェクト指向の隠蔽化を行っていれば、当初のクラス設計が悪くて後で変更 したくなった場合、利用者側からの見た目の動作を変更せずに、内部のデータ構造や処理メソッドを 変更が可能となる。 このように、利用者側からの見た目を変更せずに処理の内部を変更すること、 リファクタリング と呼ぶ。これにより、プログラムの不備や問題点があっても、積極的にプログラムを 改良できることから、不備の少ない安全なプログラムを作れるようになる。
隠蔽化の課題
以上の2つのプログラムで複素数の計算メソッド、加算(add),除算(sub),乗算(mul),除算(div)…その他を (実部,虚部)、(絶対値,偏角)で記載し、適切に記述をすれば、呼び出し側main()を まるっきり同じものにできることを通して、隠蔽化についてレポートにまとめよ。
レポートでは、以下の点を記載すること。(レポートは、本科中間試験の頃までに提出が望ましい)
- 2つの方式でのプログラム例
- 上記プログラムに対する説明
- 上記プログラムが正しく動作していたことが判る結果
- この課題から判る考察