これは少し広範で概念的な質問です。
いろいろなメソッドのクラスがあります。A
それらをと呼びましょうB
。将来このクラスで作業する他の開発者が、最初にメソッドAを少なくとも1回呼び出す前に、メソッドBを呼び出さないようにするにはどうすればよいですか?
私はこれをC++で行っていますが、一般的にこれを強制するための最良の方法は何ですか?ブール変数を使用するなど、いくつかの素朴なアイデアがありますが、他の考えも聞きたいです。
これを保証する1つの方法は?メソッドAを1回呼び出すのは、メソッドBの責任にします。
それ以外は壊れやすいAPIです。
A
が呼び出されたかどうかを判別するブール変数があります。次に、このブール変数を設定せずに誰かが呼び出そうとするとB
、IllegalStateExceptionがスローされます。
または、とにかく最初に呼び出されないと実行できないように見えるので、B
単に呼び出すこともできます。A
A
それ以外の場合、および両方のメソッドが公開されているため、これを強制する他の方法は実際にはありません。
ブール値を使用することは良い出発点であり、アクセスをスローしても問題ありません。
ただし、コンパイル時にこれを強制できると便利な場合があります。その場合、唯一の現実的なオプションは、いくつかのトリックを使用することです。
クラスで A のみを公開し、B を含むプロキシを返すようにします。
class MyClass {
public:
struct BProxy {
public:
MyClass * root;
void B() { root->B(); }
protected:
BProxy( MyClass * self ) : root(self) {}; // Disable construction
friend class MyClass; //So that MyClass can construct it
};
BProxy A() { ... return BProxy(this); }
friend class BProxy; // So that BProxy can call B()
protected
void B() { ... }
};
int main() {
MyClass m;
BProxy bp = m.A();
// m.B(); can't do this as it's private - will fail at compile time.
bp.B(); // Can do this as we've got the proxy from our previous call to A.
}
B() を実装する (または仮想を提供する) ベースクラスからの保護された継承を使用して、同様のことを実現することもできます。
1 つの方法は、クラスを少し異なる方法で再設計することです。使用する前に初期化する必要がある単純なデータベース クラスを考えてみましょう。私はJavaの男なので...
public class Database {
public void init(String username, String password) // must call this first!
public List<Object> runQuery(String sql) // ...
}
したがって、最初に init を呼び出す必要があります。初期化を行い、実際のデータベース オブジェクトを返す DatabaseFactory を作成できます。コンストラクターを非表示にして、DatabaseFactory のみがデータベースを作成できるようにすることができます (Java ではネストされたクラス、C++ ではフレンド クラスでしょうか?)。
public class DatabaseFactory {
public Database init(String username, String password) // ...
public class Database {
private Database() {}
public List<Object> runQuery(String sql) // ...
}
}
そのため、基になるオブジェクトに到達するには、Factory を通過する必要があります。
DatabaseFactory factory = new DatabaseFactory();
Database database = factory.init("username", "password"); // first init (call method A)
// now I can use database (or B in your case)
database.runQuery("select * from table");
メソッド「A」をコンストラクターにして、オブジェクトを初期化します。これは、コンパイラによって強制されたオブジェクトを使用するために一度呼び出す必要があります。後で、コンストラクターが呼び出されている必要があることを認識して、メソッド「B」を呼び出すことができます。
それを保証する 1 つの方法A
は、クラスのコンストラクターで行われることです。コンストラクターが失敗 (スロー) した場合、他の開発者はそれをどうするべきかについて何も持っていませんB
。コンストラクターが成功した場合、A
少なくとも 1 回は実行されるため、B
実行する有効な操作です。