protectedを使うと依存関係が複雑化してしまう、という例を並べてみました。privateメソッドにも言及しています。異論や例外はあると思いますが、こういう考え方もあると知っておくと役には立つと思います。
そのprotected変数いらなくない?
C++やC#のprotected変数は使うべきではない、というのは一般的な意見かと思います。Effective C++でも言及されていますね。protected変数を持つClass Aを継承したClass Bにとっては、Class Aのprotected変数の内容を知らなくてはならなくなるし、Class Aの関数によってprotected変数の内容が変更されるかどうかは実装を見ないとわからない。これは、Global変数に依存するのと同様に依存関係の複雑化を招きます。
class A{
protected:
int _value;
public:
void Reset(); //関数によって_valueが変化するかは実装を見ないとわからない
}
class B : public A{
B() : A(){
_value = -5; //Aが想定していない形で_valueが設定されてしまう可能性がある
}
}
というわけで、変数はprivateにしましょう。継承先で値の参照が必要になったら、メソッドを準備すればOK。
そのprotectedメソッドいらなくない?
protectedメソッドを使う場面も、よく考えたらis-a関係じゃなくhas-a関係だった、ということが多いです。例えば、下記のようなコードはStrategyパターンで実装した方が適切です。
//継承により実装を柔軟に変更可能としたい場合
class A_inheritance{
protected:
virtual void Implement() = 0;
public:
void Func(){
Initialize();
Implement();
Terminate();
}
private:
void Initialize();
void Terminate();
}
class B : public A_inheritance{
protected:
void Implement(){
//実装内容
}
}
//上記より、Strategyパターンで書いた方が適切
class A_strategy{
private:
const IImplement &_impl;
public:
A_strategy(const IImplement &impl)
: _impl(impl)
{}
void Func(){
Initialize();
_impl.Implement();
Terminate();
}
private:
void Initialize();
void Terminate();
}
変数と合わせて、protectedを使うときはちょっと手を止めてみましょう。
そのprivateメソッドいらなくない?
privateメソッドはpublicメソッドのサブルーチンとして使うことが多いと思います。privateメソッドは、そのクラス内のすべての変数を使うことができてしまいます。これでは、メインルーチンから見るとサブルーチンの入力が何なのか判断がつかなくなってしまいます。
多くの場合privateメソッドの代わりにstatic関数を使うことで、サブルーチンの入力を明確化でき、わかりやすくなります。
static int SomeStaticFunc(int value, int times){
return value * times;
}
int SomeClass::PrivateFunc(){
return _value * _times; //private変数を用いている
}
//privateメソッドを使った場合
void SomeClass::PublicFuncWithPrivate(){
if( PrivateFunc() > 100 ){
//実行内容
}
}
//staticメソッドを使った場合
void SomeClass::PublicFuncWithStatic(){
if( SomeStaticFunc(_value, _times) > 100 ){
//実行内容
}
}
PublicFuncWithPrivate
より、PublicFuncWithStatic
の方が、用いている変数も一目でわかりますよね。この意見あんまり見ないですが、static関数の方がよくないですか?
C#は単独の関数が使えないので、代わりにprivate staticメソッドか、別クラスのpublic staticメソッドになります。
その言語、protectedなくない?
protectedはいつから実装されたのか、興味が湧いたので調べてみました。
オブジェクト指向を最初期に導入し、C++に影響を与えたのがSIMULA(wikipedia)という言語らしいのですが、その初期実装の一部から既にprotectedはあったようです。C++がその影響を受けprotectedを導入し、JavaもC#も導入し、他の多くの言語も影響を受けて、今の形があるようです。
一方で、SwiftとかRustとかPythonとか、一部の言語は文法的にprotectedの役割をするものがないですよね。protectedをサポートしなかった理由は言語それぞれなので乱暴な言い方になってしまいますが、これらの言語の存在が、オブジェクト指向においてprotectedなしでも実用上問題ないことの証明になっています。