くそコードが無くなる日を目指して

実例はC++/Javaがメインです|良いコードは健康を増進します

クラスを継承したときの初期化(コンストラクタ等)と対になる終了処理(デストラクタ等)の話。

初期化の順番ってどれほど気にしているでしょうか?例として、
public class InitializePattern1 extends Parent {
    public InitializePattern1() {
        initializePattern1Class();
        super();
    }
}


public class InitializePattern1 extends Parent {
    public InitializePattern1() {
        super();
        initializePattern1Class();
    }
}

だったら、どっちが良いと思いますか?どっちでも良いと思いますか? 
前者はコンパイルエラーになります(コンストラクタなので、たぶん)。

なぜか。

親を継承した子クラスは親クラスが存在して初めて成立するので、
親クラスが準備出来ていない時に子クラスが何かする(この場合は初期化する)と、
親クラスの初期化されていないメンバにアクセスしたりして、予期しない動作を起こす可能性がでてきてしまうからです。
→だから順番が強制されているのです。(終了処理は必然的に順番が逆になります)
ちなみに親が子のことを予め知ることはできないので、親→子の順番で初期化するのは何も問題おきません・・・。
(こういう理由でprivateなコンストラクタを持つクラスは継承できないのです。つまり、コンストラクタにつく「private」は継承を禁止にするキーワードでは全くなく、コンストラクタがprivateだと呼べないから結果的に継承できなくなってるだけなのです)

この例はコンストラクタなので、当たり前なところがありますが、最近流行り?のAndroid界隈では結構こんなコードを見かけます。
public class SampleActivity extends Activity {
    @Override
    public onCreate() {
        doInitialization(); // 先に初期化しちゃってる
        super.onCreate();
    }

    public onDestroy() {
        super.onDestroy(); 
        doClosing();// 後で終了処理しちゃってる
    }
}
これは、コンストラクタやデストラクタ(C++)以外は、特に順番関係なく呼べちゃうからできちゃうんですね。
でも上で言った話があるので、本当は逆にしたほうがいいです。

最後に関連して一つ追加すると、
初期化と終了は対になっているはずなので、A->B->Cの順番で初期化したら
C->B->Aの順番で終了されているのが正しいです。
(もちろん互いに関係なければ順番は関係ないですが、お作法的な配置の話です)
public class SampleActivity extends Activity {
    @Override
    public onCreate() {
        super.onCreate();
        doInitializationA(); // Aを初期化
        doInitializationB(); // Bを初期化
        doInitializationC(); // Cを初期化
    }

    public onDestroy() {
        doClosingC(); // Cを終了
        doClosingB(); // Bを終了
        doClosingA(); // Aを終了
        super.onDestroy();
    }
}

まとめると、初期化は親→子、終了処理は子→親の順番でやるのが正しくて、順番が逆なのはなにも気にしていないか、よほどの理由があるときしかありえない。

ぜひ気をつけましょう。

 

名前付けは重要。そんなことはわかってるという人はたくさんいると思います。
でも、中身/内容が同じだったら全部同じ名前でいいと思っていたりしないでしょうか?

細かいことを気にしなければ、確かに中身が同じコード=同じメソッド名、変数名でもいいかもしれません。でもこのブログは 、くそコードが無くなる日を目指している手前、そんな中途半端なことを、やってOK!とは口が裂けても言いません。

具体的にどういうことか、ちょっと例を示しながら説明したいと思います。

TVが付いているという状態を表現したいとします。
特に何も考えないのであれば、
 
public class XXX {
    ...
    boolean mIsTvOn = true;
}
のようにするわけです。良さそうに見えます。
でもこれで問題ないかどうかはXXX次第です。
XXXがLivingRoomだったとします。
 
public class LivingRoom {
    ...
    boolean mIsTvOn = true;
}
リビングのテレビがついている。至って自然です。
ではXXXがTv(テレビ自身)だったらどうでしょう?
 
public class Tv {
    ...
    boolean mIsTvOn = true; // Tv自身がついている?それとも別のTvがついている?
}
少し混乱します。「TVがついている」という状態は、文章でみると無意識的に想像して、表現が同じになりそうですが、実際は誰から見るかで、表現のされ方が変わらなければいけないとわかります。
つまりTvから見れば、自身がついているときは、自分自身がTvなことは自明なので、 
public class Tv {
    ...
    boolean mIsTurnedOn = true; // Tv自身がついている
}
とすべきといえます。

一言でまとめると、「視点の違いで適切な名前は変わる」ということです。
こういった名前付けはメソッドでも同じです。

是非気をつけてみてください。 

オープンソースの世界では、GPLを始めとして、ApacheライセンスやMITライセンスなどなど、Copyleftなライセンスがよく使われています。

このブログもくそコードが無くなる日を目指して、皆さんと知恵や経験を共有するため、記事をクリエイティブ・コモンズ・ライセンスで公開しています。

ライセンスの詳しい中身はブログ内のバナーから見られますが、
簡単には、記事(ソースコード含む)の出処が明記されていれば転載等、商用・非商用問わず利用は自由というライセンスです。

記事はどんどん使って構わないので、是非、使ってください。

オープンソースの世界のライセンスの話はまた別のときに書こうと思います。

↑このページのトップヘ