C#でクラスを作ろう(9)/抽象基本クラス
C言語やC++言語などを学んではいるけどクラスをあまり作ったことが無い、という方を対象にしています。
このシリーズでは、C#でクラスを作るための基本的な構文を解説しています。C++やJavaなどと共通している概念も多いですが、サンプルコードは基本的にC#で解説します。ところどころ、C++特有の概念を解説することもあります。
前回のC#でクラスを作ろう(8)/クラスの継承で、既存のクラスを継承して機能を拡張する方法を解説しました。前回は、Person(人物)という既に存在するクラスを継承して小学生(Student)という新しいクラスを作る例を解説しましたが、今回はもう1つ典型的な継承の例を解説します。そして、そこでよく使われる「抽象基本クラス」という概念について解説していきます。
似た機能を持つクラス
まず初めに、1つのファイルのコピーを担うクラスを考えます。
class LocalFileCopyItem
{
public string SourceFilePath { get; set; } //コピー元のファイルパス
public string DestinationFilePath { get; set; } //コピー先のファイルパス
public void DoFileCopy()
{
//ファイルコピーの実行
File.Copy(this.SourceFilePath, this.DestinationFilePath);
}
}
プログラムを拡張していく中で、ファイルをFTP送信する必要が出てきたとしましょう。そんなとき、次のようなクラスを作ることになります。
class FtpFileCopyItem
{
public string SourceFilePath { get; set; } //コピー元のファイルパス
public string DestinationURL { get; set; } //コピー先のURL
public string FtpUserName { get; set; } //FTP接続のユーザー名
public string FtpPassword { get; set; } //FTP接続のパスワード
public void DoFileCopy()
{
//FTP送信の実行
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(this.FtpUserName, this.FtpPassword);
wc.UploadFile(this.DestinationURL, this.SourceFilePath);
}
}
FTP送信の実際のコードについては詳しく解説しませんが、大体こんな感じです。さて、このFTP送信を担うFtpFileCopyItemクラスと、先に作ったローカルコピーを担うLocalFileCopyItemクラスは、どのような関係にあるでしょうか?
似たような機能でありながら、どちらがどちらから機能を継承しているともいえません。図で書くなら、以下のような関係になっているといえます。
ということはつまり、この図の通りにクラスを継承すればいいのです。
//何らかの共通部分
class FileCopyItem
{
public string SourceFilePath { get; set; } //コピー元のファイルパス
}
//ローカルコピーを担うクラス
class LocalFileCopyItem : FileCopyItem
{
public string DestinationFilePath { get; set; } //コピー先のファイルパス
public void DoFileCopy()
{
//ファイルコピーの実行
File.Copy(this.SourceFilePath, this.DestinationFilePath);
}
}
//FTP送信を担うクラス
class FtpFileCopyItem : FileCopyItem
{
public string DestinationURL { get; set; } //コピー先のURL
public string FtpUserName { get; set; } //FTP接続のユーザー名
public string FtpPassword { get; set; } //FTP接続のパスワード
public void DoFileCopy()
{
//FTP送信の実行
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(this.FtpUserName, this.FtpPassword);
wc.UploadFile(this.DestinationURL, this.SourceFilePath);
}
}
「何らかの共通部分」をFileCopyItemクラスとして抜き出し、このFileCopyItemを親クラスとして持つ二つの子クラス、LocalFileCopyItemとFtpFileCopyItemを派生させました。
ここで、DoFileCopyメソッドの立ち位置が微妙ですよね。ファイルをコピーするという「やること」は同じだけど、内部の実装は全然違う。共通部分と言えるかもしれないし、言えないかもしれない。このような「やること」が同じメソッドをどう扱うかについては、次回のポリモーフィズムで詳しく解説します。
いずれにしても、このように既存のクラスに対して似た機能を持つクラスが必要になったときは、その共通部分を抜き出して新しい親クラスを作り、既存のクラスと似た機能を持つクラスの両方をその親クラスから派生させるという手法がよく使われます。
親クラスが抽象的なとき
さて、この共通部分として親クラスに格上げされたFileCopyItemクラスですが、これは一体どういう性質を持つクラスなのでしょうか。
具体的なLocalFileCopyItemクラスやFtpFileCopyItemクラスのように、どうやってファイルをコピーするかは定義されていません。ただ抽象的に、「ファイルをコピーするという概念の基本部分」を抜き出しただけのものです。従って、FileCopyItemクラスのオブジェクトを作成しても、それは何の意味も持ちません。
つまり、この親クラスFileCopyItemは抽象的なのです。そこで、抽象的であることを明示するために、キーワードabstractを追加します。
abstract class FileCopyItem
{
public string SourceFilePath { get; set; } //コピー元のファイルパス
}
//オブジェクトの生成
FileCopyItem item = new FileCopyItem(); //コンパイルエラー!
abstractキーワードが付けられたこのFileCopyItemは、抽象クラスとなります。抽象クラスのオブジェクトは生成することができません。従って、"new FileCopyItem();"はコンパイルエラーとなります。
抽象クラスは、別の具体的なクラスに継承されることによって初めて、オブジェクトを生成することができます。抽象クラスは必ず何かの基本クラス(親クラス)となるので、抽象基本クラスとも呼ばれます。
abstract class FileCopyItem { //略... }
class LocalFileCopyItem : FileCopyItem { //略... }
//オブジェクトの生成
LocalFileCopyItem item = new LocalFileCopyItem(); //これは生成可能
これと対照的に、前回のPerson(人物)クラスのように、それ単体でも具体的な意味を持ってインスタンス化できる(オブジェクトを生成できる)親クラスのことを、具象基本クラスと呼んだりします。
また宗教論争的な話になりますが、「具象クラスからは継承してはいけない」とする学派もあります。このあたりは、言語・時代・環境によって考え方が変化するので、自分の所属するプロジェクトやチームの考え方に合わせるようにしましょう。
C++にはabstractというキーワードはありませんが、先の章で紹介する純粋仮想関数を持ったクラスは自動的に抽象的となり、インスタンス化できなくなります。
継承と多態性
前回と今回の解説だけでは、共通部分を抜き出してコードの重複を避けていることにしかならず、クラスの継承の有用性がまだ見えてこないかもしれません。
クラスの継承というものは、ポリモーフィズム(多態性)を備えることによって本当の威力を発揮します。それについてはまた次回以降に。
ディスカッション
コメント一覧
まだ、コメントがありません