ブログ「サイバー少年」

ブログ「サイバー少年」へようこそ!
小学六年生ごろからプログラミングを趣味にしている高校生のブログです。
勉強したことについての記事などを書いています。フリーソフトも制作、公開しています。
(当ブログについて詳しくは「ブログ概要紹介」を参照)

サイバー少年が作ったフリーソフトは「サイバー少年の作品展示場」へ

クラスの継承をポリモーフィズム以外で利用する事例

一応、C#とVB.NETを想定した内容ですが、だいたいJavaなどにも通用すると思います。


オブジェクト指向言語にはクラスの継承という機能があります。

これは、既存のクラスの内容を参照し、さらに独自の処理を加えたクラスを作成する機能です。

しかし、
既存のクラスに新しいものを付け加えたいから継承を使う
なんてことはないんですよ。

だって、既存のクラスをコピペして編集すればいいんですから。

継承を使うなんて、抽象クラスのポリモーフィズムをやりたいときだけ

私は今までそう思っていました。

しかし、本当は違ったのです。

そう、本当は…。



小説のまえがき風になってしまいましたが(笑)

前、Yahoo!知恵袋で質問したんですよ。
「ポリモーフィズム以外で継承を使うのはいつですか~?」
って。

そしたら、「“マーカーインターフェース”というものに使う」と回答がありました。

クラスが特定の機能をもっているか確認するために使うんだそうです。
わしはようわからん。


さて、継承なんて抽象クラスでポリモーフィズムを実現したいときぐらいしか普通使いません。
機能の追加なんかに使いませんよ。いや、多分。

しかし、そんなポリモーフィズムの引き立て役みたいな位置づけで継承を使おうものなら、
継承様が黙っていません。

では、ポリモーフィズムと関係なく、継承を使うと便利なものはどんなものがあるでしょうか。

一つずつ挙げていきます。



継承をナメるなよ!
クラスの継承をポリモーフィズム以外で利用する事例
~C#を想定しています~

1.生成アセンブリのサイズを小さくするために使う

アセンブリが分からない方、
アセンブリとはコンパイルしたEXEファイルのことです。

さて、既存のクラスに新たな独自機能を加え、加える機能の種類で枝わかれさせたい。
よくあることです。

たいてい、このような場合はベースクラスを抽象化してオーバーライドを盛り込むので、継承を使うことになります。

では、オーバーライドなんて使わずに、
単純に様々な種類のクラスを枝わかれさせたいときは継承なんていらなくて、
コピペをすればいいのでしょうか。

いいえ、そういう場合でも継承を使うほうがいいです。

そうすると生成されるアセンブリのサイズが小さくなるのです。

コピペの場合、共通する部分をコピーすることになりますが、
すると同じ処理をいくつも書いてしまうことになります。

実質的には同じ処理でも、コンパイラから見たら全く違う処理です。

ですから、コピーした分コンパイルすることになり、これは無駄です。

そこで、共通した処理を一つにするために継承を使います。

実は継承とコピペは同じではないんです。

コピペしたコードは同じ処理をしていますが、違うものとして扱われます。

しかし継承は、同じ処理の部分を一つのものとしてコンパイルし(ここがベースクラス)
独自機能を追加したクラスから、共通した処理を呼び出した際は、
ベースクラスのインスタンスに参照して呼び出すことで
サイズを節約する仕組みになっています。

つまり、かぶっているところを共通化しているんですね。
 
いくつ継承させても、サイズを大きくするのは追加機能の部分だけですから、
共通機能のところは一つでいいんです。

アセンブリのファイルサイズが小さくなったということは
アプリケーション起動時のメモリ消費量も少なくなります。

しかし、インスタンス作成時はベースクラスと継承クラスの両方を作らないといけないので、
実行時に継承でメモリ消費量を抑えることはできません。

ですが、生成されるアセンブリファイル(EXEファイル)のサイズと、
アプリケーションの起動に必要なメモリ量は少なくなります。

同じ処理を一つにするというのは、メソッド化手法と似ています。

メソッド化手法も、無駄に同じ処理をいくつもコンパイルすることを抑止できるので、生成アセンブリのサイズを小さくすることができます。

ですから、継承はメソッド化手法をクラスレベルで実現したものと言えるでしょう。

ちなみに、アセンブリレベルでのメソッド化手法は、処理のライブラリ化によって行いますね。


2.クラスの更新を反映させるために使う

これは項目1.の補足的なものなんですが、別項目として書きます。

項目1.で説明したのは、機能を枝分かれさせたいときに継承を使うと、生成アセンブリのサイズが小さくなるということでしたが、
このときに、もう一つメリットがあります。

共通機能の部分でバグがあったり、動作を変えたかったりした場合、

コピペの場合は書きかえたコードを、コピペしている全てのクラスで反映させないといけませんが、

継承を使った場合、全て書きかえ元に参照しているため、一つ一つ書きかえる必要がありません。

対象が、数個のクラスならどっちでも良いかも知れませんが、
これが何十個というクラスの数になったときは、コピペがかなり大変な作業になります。

(コピペのとき、自動で書きかえてくれるツールがあれば解決しますが、そこまでするなら継承を使ったほうが便利です。)


3.設定変更をクラスが包括するために使う

私が、継承が3.の用途に利用できると気づいたときは驚きました。

.NET FrameworkのFormクラスも、これを活用しています。

Visual Studioでフォームを新規作成すると、Formクラスではなくて、
わざわざFormクラスを継承したForm1クラスが生成されます。

なぜかというと、
一つはコントロールを内部的に管理できるようにするためです。

Buttonなどのコントロールをフォームに追加する際、継承をせずにやると
コントロールの管理を外部クラスがやることになり、連携をしないといけなくなるので、とても不格好なコードになってしまいます。
 
Form1と一緒にForm1_Controlsクラスが作成される感じです。

もう一つはFormクラスに与える大量の設定変更を内部的に包括するためです。

Formには大量のコントロールを貼り付けます。

そのため各コントロール(Form自身も含む)の膨大な数のプロパティを設定していくこともよくあります。

そのときに、外部から一つずつ設定していては、Formの宣言時に

宣言
プロパティ
プロパティ
プロパティ



と言うふうにプロパティの変更だらけのコードになってしまいます。
これは読みにくいし分かりにくいです。

これは、プロパティの変更をメソッド化すればいいと思うのですが、

すると、異なるFormを利用する場合「Form1用のメソッド」や、「Form2用のメソッド」など
専用のメソッドがたくさん作られてしまい、これも分かりにくいです。

また、Formをメンバとして持って、さらにプロパティを変更するクラスを作ろうとした場合は、メソッド化したときと同じような問題が起きます。

そこで継承を使います。

プロパティを変更しない基本フォーム(Form)を作って、
プロパティ変更の処理を書いたコードをクラスにして、
それにFormを継承させればいいのです。

ややこしいことを言いましたが、要するにForm1のことです。

フォーム自信や、各コントロールのプロパティ変更をForm1が担っています。

これでFormを呼ぶ側は何も意識しなくてよくなりますね。

設定変更がどうたらと言う話ではなく、すでに設定変更されたFormを利用できるんです。

このやり方がそのまんまForm1に利用されています。


話を変えますと、
本来Formもインスタンスメンバであり、Formの存在を意識する必要があるのですが、
C#やVB.NETではその必要がありません。

なぜなら、外部からFormを操作することが無いからです。
これは外部と連携せず、上記のように全てのことをForm(Form1のこと)が担う設計なためです。

Formがインスタンスメンバであると意識する必要があるのはForm外部での話です。
Form内部ではそんなことを考えないでいいのです。

だいぶ役に立っている機能です。




以上で、クラスの継承をポリモーフィズム以外で利用する事例は終わりです。

これに加えてポリモーフィズムにも貢献しているのですから、継承が素晴らしいものに見えてきます(笑)

みなさんも、コードを書いていて「ここなんか、もっといい方法ないかな」と思ったときは
継承のことを考えてみてください。

継承は予想以上に素晴らしいヤツです。



P.S. 愚痴

実はこの記事は、2週間以上前に書き始めていて、コツコツ書いてやっと完成しました。

完成が遅かったのには、記事の編集をかなりサボっていたのもあるのですが、
ブログ記事とは言えないほどの大作が出来上がりました。

いや~、文章書くのって時間が掛かるものですね。
小説作家の方なんて大変だと思いますよ。

tag:

コメント

似た処理をたくさんの場所に書くのは
保守の面でも問題がありますね。
DRY原則に反してますし。

  • 2013/01/07(月) 11:06:56 |
  • URL |
  • wisdom103 #-
  • [ 編集 ]

Re: wisdom103

確かにそうですね。
DRY原則に思いっきり反していますから、後に面倒臭いことになります。

項目2.にて解説しています。

コメントの投稿

トラックバック

トラックバック URL
http://cyberboy6.blog.fc2.com/tb.php/203-b1ff512e
この記事にトラックバックする(FC2ブログユーザー)

当ブログをご利用(閲覧等)になる場合は必ず「当ブログの利用規定」をお守りください。