オブジェクト指向プログラミング十戒 4 むやみに内部をさらさない


 

4 むやみに内部をさらさない

クラス内でしか使用しないメソッドを public にするなどして無用に内部をさらさない。知らないところで使用されていて、メソッドの改修が思わぬ影響を及ぼすことになりかねない。

Change.java
public class Change { public void doProc(String s) { int n = change(s); // int に変更した // 略 return; } // short から int に変更した protected int change(String s) { int n = 0; // 略(変換処理) return n; } }
Change2.java
public class Change2 extends Change { public void doProc(String s) { short n = change(s); // int への変更をしていない // 略 return; } }

Change クラスの change をクラス内だけで使用するつもりで short 型で作成しました。ただ、protected としたため Change クラスのサブクラスからは参照できてしまいました。そのため、サブクラスの Change2 から意図しない参照がされていました。

この後、Change クラスの change を int に変更し、参照している部分も修正しました。

しかし、Change2 クラスから参照されていることを知らないので、Change2 クラスは修正しませんでした。その結果、Change2 クラスがエラーとなってしまいました。

この例ではエラーとなったのですぐわかりましたが、戻り値の型ではなく意味を変更したような場合はエラーとなりませんので見つかりにくいバグとなる可能性があります。

Change.java
public class Change { public void doProc(String s) { int n = change(s); // int に変更した // 略 return; } // short から int に変更した private int change(String s) { int n = 0; // 略(変換処理) return n; } }

change は private なので、他のクラスからは参照されません。そのため、他のクラスからの意図しない参照は行われません。


継承されたくないクラスやオーバーライドされたくないメソッドには final を記述して書き換えを許さない。本来のメソッドの意図とは異なるものに書き換えられてしまうことを防ぐ。

Authority.java
public class Authority { private boolean authority = false; public boolean isValidPassword(String id, String pw) { String passWd = getPassword(id); authority = passWd.equals(pw); return authority; } public boolean isAuthority() { return authority; } private String getPassword(String id) { // データベースから読み込む(この例では固定値) return "AAA"; } }
RogueAuthority.java
public class RogueAuthority extends Authority { public boolean isValidPassword(String id, String pw) { return true; } public boolean isAuthority() { return true; } }
Process.java
public class Process { public void execute(Authority a) { if (!a.isAuthority()) return; // 略(権限があるときの処理) } // 略 }

Authority クラスではパスワードが合っていれば、いろいろなことができる権限が与えられます。ただ、パスワードのチェックをする isValidPassword や権限があるかどうかを調べる isAuthority は、サブクラスでオーバーライドできるので、パスワードが何であっても権限があるようにできてしまいます。

よって、Process クラスの execute に Authority ではなく RogueAuthority のインスタンスを渡すと、execute は権限があるかのように振舞うことになります。

Authority.java
public final class Authority { private boolean authority = false; public boolean isValidPassword(String id, String pw) { String passWd = getPassword(id); authority = passWd.equals(pw); return authority; } public boolean isAuthority() { return authority; } private String getPassword(String id) { // データベースから読み込む(この例では固定値) return "AAA"; } }
RogueAuthority.java
public class RogueAuthority extends Authority { public boolean isValidPassword(String id, String pw) { return true; } public boolean isAuthority() { return true; } }

オーバーライドされたくないメソッド(あるいはクラス)には、final を記述します。

この例では、Authority クラスに final を指定したので、このクラスを継承したサブクラスは作成できなくなります。そのため、継承しようとするとエラーになります。

また、メソッドに final を指定すると、そのメソッドをオーバーライドすることができなくなります。