重複

2012-01-27 12 views
0

せずに、オブジェクトの子のメソッドを公開私はそれがその状態(例えばEntityManagerInputManagerPhysics)を管理するために使用するいくつかのサブシステム(子オブジェクト)を持つクラスLevelのオブジェクトを持っています。私はLevelの外部インターフェイスのサブシステムのメソッドのいくつかを公開したいと思います。重複

uint32_t Level::CreateEntity() 
{ 
    return entityManager.CreateEntity(); 
} 

Entity& Level::GetEntity(uint32_t entityId) 
{ 
    return entityManager.GetEntity(entityId); 
} 

uint16_t Level::CreateInputState() 
{ 
    return inputManager.CreateInputState(); 
} 

void Level::AttachInputState(uint32_t entityId, uint16_t inputStateId) 
{ 
    inputManager.AttachInputState(entityId, inputStateId); 
} 

InputState& Level::GetInputState(uint16_t inputStateId) 
{ 
    return inputManager.GetInputState(inputStateId); 
} 

このソリューションはLevelクラス内のメソッド宣言を複製し、サブシステムに制御をリダイレクトする1行の呼び出しを書くために私を必要とします。

は、ここに1つのソリューションです。私が過去に取り組んできたプロジェクトでは、これは管理が面倒です。

もう1つの解決策は、パブリックインターフェイスでサブシステムを公開することです。 Levelの外にあるオブジェクトがコールを転送するサブシステムにとって重要ではないので、これを避けることができます。

この問題をよりエレガントに処理できるデザインはありますか?

+0

子供の機能のすべてを公開したいのですか、特別な機能のみを公開しますか? – Nobody

+0

なぜマネージャオブジェクトは 'Level'のメンバである必要がありますか? – wilhelmtell

+0

@Nobody特別なもののみ。 – Kai

答えて

1

私はOPの要求に応じて私がコメントで提案した解決策を投稿します。 新しいC++標準のテンプレートを使ったほうが良いとは思っていますが、とにかく私は醜いプリプロセッサソリューションを投稿し、使用すべきではありません!

#define FUNCTION_DECLARATION(RETURNTYPE, FUNCTIONNAME, ...) \ 
    RETURNTYPE FUNCTIONNAME(__VA_ARGS__) 

これは、ように、クラスの宣言に使用される:

#define FUNCTION_DEFINITION(RETURNTYPE, PROPERTY, FUNCTIONNAME, ...) \ 
    RETURNTYPE Level::FUNCTIONNAME(__VA_ARGS__) \ 
    { \ 
     return PROPERTY.FUNCTIONNAME(__VA_ARGS__); \ 
    } \ 

そして、この作品を作るために今、非常に醜い使用::定義は次のようになり

class Level { 
    FUNCTION_DECLARATION(uint32_t, CreateEntity); 
    FUNCTION_DECLARATION(Entity&, GetEntity, uint32_t); 
}; 

FUNCTION_DEFINITION(Entity&, entityManager, GetEntity, uint32_t(entityId)) 

保証できませんこれは、私がコードの大部分をテストしていないすべての型に対しても機能することになります。あなたが見ることができるように、この "ハック"は入力では、単純な型であり、参照やポインタでは機能しません。クラス上では、コピーコンストラクタをトリガします!

もちろん、VA_ARGSを型の変数と関数の各引数の可変名に置き換えることでこれを改善できますが、これもまた非常に面倒です。利用される。結果としてこれまでと同じくらい多くのコードが得られます。

もう一度言わせてください:これではるかに優れたテンプレートを貼ってください! 私はそれをどうやってやるのか分かりません。だから、テンプレートを使ってそれをする方法を知っている人がいる場合は、私にこれを打つ代わりに**コードここに誰もが痛いです。

+0

ああありがとう!マクロコード全体を打ち込むことを意味するわけではありませんが、それにもかかわらず見ておくと便利です。 – Kai

0

ザ・は他の目的はEntityManagerPhysicsなどLY privateが含まれている私は、インターフェイスがよりエレガントになるだろうと知って持っていると思うし、それはpublic LYしたいの各機能を呼び出す機能を公開しているクライアント基礎となるオブジェクトを呼び出すことができます。 Levelには、これらのプロキシオブジェクトのインスタンスがパブリックインターフェイスに存在するようにします。オプションで、これらのプロキシをコピー不可、移動不可などにすることもできます。

これにより、 )、インターフェイスをより整理しやすく使いやすくします。

例:

class Physics { 
public: 
    T gravity() { ... } // we want them to be able to call this 

    T2 nope() { ... } // but not this 
}; 

class LevelPhysics { 
public: 
    T gravity() { return phys.gravity(); } 

private: 
    LevelPhysics(Physics& phys) : phys(phys) { } 

    Physics& phys; 

    friend class Level; 
}; 

class Level { 
public: 
    LevelPhysics GetPhysics() { return LevelPhysics(phys); } 

private: 
    Physics phys; 
}; 

は、その後、残念ながら "あなたのためにこれらを作る" への言語機能はありません

const LevelPhysics& phys = lvl.GetPhysics(); 

phys.gravity(); 
// but you can't use Physics::nope 

のようにそれを使用することができます。あなた自身で何かコーディングをすることはできません。

あなたがPhysicsの秘密のメンバーにアクセスするために必要なクラスのすべてを知っていれば別の方法として、あなたは、単にこれを行うことができます:

class Physics { 
public: 
    T gravity() { ... } 

private: 
    T2 nope() { ... } 

    friend class Level; 
}; 

class Level { 
public: 
    Physics physics; 
}; 

その後

lvl.physics.gravity(); 
// but can't do lvl.physics.nope();, only Level can 
+0

これは私にはインターフェイスとコードの重複を複雑にするようです。 – Kai

+0

どのようにインターフェイスが複雑になりますか?より良い組織と引き換えに、1行にもっと多くのコードを必要とすることは、それを私に単純化するようです。あなたが私の答えに追加する何かをしたいのでなければ、この作業のためのコードの重複の周りに方法はありません。 –

+0

@Kaiそこに、あなたの既存のコードがそれを可能にするかどうか見てください。 –

1

フィーチャを集約するもう1つの方法は、プライベート継承を使用する方法です。次に、継承されたプライベートメソッドの一部をusingディレクティブを使用してパブリックメソッドに変換できます。例えば、

#include <iostream> 

class feature_A 
{ 
public: 
    void func_A1() { std::cout << "A1" << std::endl; } 
    void func_A2() { std::cout << "A2" << std::endl; } 
}; 

class feature_B 
{ 
public: 
    void func_B1() { std::cout << "B1" << std::endl; } 
    void func_B2() { std::cout << "B2" << std::endl; } 
}; 

class compound : private feature_A, private feature_B 
{ 
public: 
    // Provide these functions as-is. 
    using feature_A::func_A1; 
    using feature_B::func_B1; 

    // Combine these two functions. 
    void func_C2() { func_A2(); func_B2(); } 
}; 

int main() 
{ 
    compound c; 
    c.func_A1(); 
    c.func_B1(); 
    c.func_C2(); 
    // c.func_A2(); // error: ‘void feature_A::func_A2()’ is inaccessible 
} 

これらの宣言の1つの制限は、名前付きです。同じ機能の複数のオーバーロードがある場合は、1つだけをパブリックにすることを選択することはできません。テンプレートメソッドの場合も同様です。usingを使用して1つの特殊化のみを選択することはできません。

+0

私はこのソリューションが気に入っていますが、「フィーチャ」クラスの1つが純粋に仮想関数を使用すると難しいと思います。 – Kai