読者です 読者をやめる 読者になる 読者になる

A Clockwork …

~雑記メモ~

抽象クラスとインターフェイスの使い分け

抽象クラスとインターフェイスの違いってなんでしょう。

長年プログラミングに慣れ親しんでいる方は、明確にこうであると明言できるかもしれないですが、
学び始めたばかりの人であったり、あまり意識した使い方をしていない場合は使い分けが難しいかと思います。

簡単にまずは説明すると、
抽象クラスは、一部のメソッドが実装された継承して使うクラス。
インターフェイスは、定数とメソッド宣言をまとめた継承して使うクラス。

違いが解りにくいので、例を見てみましょう。

抽象クラス

public abstract class hoge {
	protected int num = 0;

	protected int getNum() {
		return num;
	}

	protected void setNum(int numTmp) {
		num = numTmp;
	}

	abstract void calc();
}


インターフェイス

public interface Ihoge {
	public int get();

	public void set();
}

大きな違いとしては、メソッドに実装が含まれているかいないかです。
抽象クラスには、継承先でも利用できるメソッドの実装が可能です。

ただ、実際には継承の仕方に違いがあったりポリモーフィズムの概念などによって、
抽象クラスとインターフェイスの意味が異なります。
中身の実装を意識しないで使えるようなクラス設計を目指すために利用されます。

詳しいことは、1冊入門書を読めば良いので割愛します。


さて本題に入ります。

では、抽象クラスとインターフェイスってどういう使い分けなのでしょうか。
基本的には、使い方として継承しないと使えないクラスで、ポリモーフィズムを意識した設計のために使いますが、
どっちで設計すればよいのか解らないかも知れません。

以降は、個人的に思っていることと経験から、
抽象クラスとインターフェイスの使い分けを説明したいと思います。
ここでの解説は、理解を深めるための考え方として参考にしていただいて、
実際にチームで開発する場合は、企業や部署などのコード規約などによって制約されていると思うので、
その規約に沿うように注意してください。

抽象クラスはどういったものに使うのか。
個人的には、クラス定義が複数存在する可能性のあるもので、
そのクラスが「進化」はあっても、「拡張」はされないものに使います。

インターフェイスはどういったものに使うのか。
個人的には、クラス定義が複数存在する可能性のあるもので、
そのクラスが、「進化」ではなく「拡張」される機能として置き換えられるものに使います。

「進化」と「拡張」という表現で説明しましたが、理解できますでしょうか。
「進化」とは、継承をするなかで次第に変化するという意味です。
「拡張」とは、規模や範囲を大きく変化させるという意味です。

「進化」する抽象クラスを使う場合は、継承を繰り返す中で、少しずつ改良されていく、
「拡張」のためのインターフェイスの場合は、機能を最終的にいくつも組み合わせるために使うイメージになります。

具体的にどういったクラスの場合であるかを説明してみます。
抽象クラスを用いるクラスの例は、動物や普遍的な物体の場合に使います。
インターフェイスを用いるクラスの例は、カメラやテレビなどの機能的な物体に使います。

さらに具体的に例を示してみます。

まずは、抽象クラスの例です。

public abstract class Animal {
	protected int age = 0;
	protected boolean die = false;

	protected int getAge() {
		return age;
	}

	protected void setAge(int ageTmp) {
		age = ageTmp;
		dieFlag();
	}

	abstract void dieFlag();
}

これを継承します。

public class dog extends Animal {
	private static int deadLine = 15;
	private String state = "normal";

	@Override
	void dieFlag() {
		if (age > deadLine) {
			die = true;
		}
	}

	public String getState() {
		return state;
	}

	public void setState(String stateString) {
		state = stateString;
	}
}

「進化」しました。
なんと、ステータスが定義されるようになりましたね。
また、すでに定義されているメソッドに関連する部分も変化が加わりました。

次は、インターフェイスの例です。

public interface ICamera {
	public void takePic();
}

public interface ITelephone {
	public void call();
}

カメラと電話のインターフェイスを使います。

public class CameraTelephone implements ICamera, ITelephone {

	@Override
	public void call() {
		// 電話操作
	}

	@Override
	public void takePic() {
		// 写真を撮る
	}
}

「拡張」しました。
なんと、カメラと電話が融合しましたね。
他に新しい機能があれば、一緒に合わせることが可能です。

伝わりましたか。

抽象クラスでは、全体的に改良して「進化」させながら開発をする場合、
インターフェイスでは、機能をあらかじめ考えて、最終的には融合させるイメージです。

個人的にはこういった感じで考えています。

ただ、実際には私自身の企業やチームで開発を行う場合では、
「すべてインターフェイスで設計する」ことが開発方針です。
一般的には、抽象クラスは内部設計が複雑になる傾向が強いので、避けることが多いと思います。

個人で開発する場合などは、今回の解説を参考にして設計してみてください。

本日は以上です。