أفضل طريقة للتعامل مع الكائن الذي لا يمكن إرساء نفسه؟

StackOverflow https://stackoverflow.com/questions/1482998

  •  18-09-2019
  •  | 
  •  

سؤال

أعتقد أنني سألت بعض الأسئلة المشابهة من قبل، لكنني كنت أضربا حول الأدغال. أعتقد أن هذه هي المشكلة الحقيقية التي لا أستطيع وضعها للراحة.

أنا أتعامل مع مكتبة الطرف الثالث, ، وهناك كائن لا يمكن أن يخلق نفسه، b2Body. وبعد ال b2World لابد ان مثيل. وبعد أنا شخصيا لا أحب هذا النمط التصميم كثيرا. اعتقد ان b2Body يجب أن تكون قادرة على الوجود بشكل مستقل للعالم، ثم تضاف إلى العالم عند الحاجة. على أي حال، لقد لفت b2Body مع فئة الدرجة الخاصة، Body, ، لأنني بحاجة إلى إضافة بعض الأشياء الإضافية إليها على أي حال. وبالمثل، لدي World غلاف. الآن أنا الرقم لدي 3 خيارات:

  1. يملك Bodyمنشئ يأخذ مؤشر World بحيث يمكن إنشاء مثيل لها بالكامل (المكالمات b2World::CreateBody في مكان ما في الداخل) - أي لدينا منشئ مثل Body *b = new Body(world_ptr)
  2. يمر Body لبعض World::CreateBody طريقة مثل كيف تقوم المكتبة بالفعل بذلك - أي Body *b = world.CreateBody(params);
  3. تكرار جميع البيانات في b2Body بحيث يمكنك استخدامه، لكنك تريد، ثم بعد إضافته إلى العالم، فسوف تبحث عن "لاستخدام b2Body البيانات - أي Body b و لاحقا world.addBody(b).

(1) و (2) يعني أنه لا يمكنك الحصول على Body بدون World, ، والتي ربما لن تحتاجها، ولكن قد يكون من الجيد أن يكون لديك هذا الخيار [حتى أتمكن من استخدامه كقالب لكائنات أخرى وهن]. لست متأكدا ما هي إيجابيات وسلبيات أخرى هناك. (3) يبدو أجمل، ولكن الأمر أكثر بكثير من العمل لتنفيذه، وهذا يعني أنه لا بد لي من تكرار معظم البيانات الواردة بالفعل في b2Body.

ما رأيك؟ مريض CW هذا فقط حتى لا الحنق واحد.


ما زلت لا أستطيع وضع هذا للراحة. هذا هو ما يبدو كل من الخيارات:

الخيار 1: (ما أفضله)

World w;
Body b;
Fixture f;
b.addFixture(f);
w.addBody(b);

الخيار 2: (في مكان ما في الوسط)

World w;
Body b(w);
Fixture f(b);

الخيار 3: (كيف box2d يفعل ذلك)

World *w = new World;
Body *b = w->CreateBody(args);
Fixture *f = b->CreateFixture(args);

لا تختلف الخيارات 2 و 3 أشخاص مختلفين، لكنها تتغير الذين يتحكمون في إنشاء الكائنات.

كيف يمكنني تنفيذ الخيار 3 في الواقع؟ World::CreateBody() يجب أن اتصل b2World::CreateBody(args) التي تدعو b2Body::b2Body() والعودة b2Body ولكن لا تدعو أبدا Body::Body(args) وهي مشكلة. ال b2Body سيحصل على تهيئة كاملة، لكن غلاف بلدي ليس لديه مكان للقيام به شيء ... بشكل خاص، كيف أكتب World::CreateBody(const BodyDef &bd)ب على افتراض BodyDef الموروثة من B2BodyDef، Body من B2body، World من B2World، إلخ.

هل كانت مفيدة؟

المحلول

أعتقد، إذا كنت ستستخدم مكتبة طرف ثالث، فيجب عليك محاربة تصميمها فقط إذا كان لديك سبب أفضل بكثير من أوه، أنا لا أحب هذا التصميم نمط كثيرا. وبعد تحتوي مكتبتك على وسيلة للقيام بالأشياء - على ما يبدو، باستخدام كائن المصنع - والقتال الذي سيزيد من تعقيد التعليمات البرمجية، وربما بشكل كبير.

نصائح أخرى

يبدو أن كائن B2World هو مصنع ل B2body، لذلك قرر المؤلف أن يكون B2body أي معنى بدون عالمه.

سيكون رد فعلي الأول أن هذه هي الواجهة، لذلك تعيش معها. اجعل جسمك العالمي هو مصنع لجسمك. لذلك هذا قريب من النهج (1) إلا أنك لم يكن لديك منشئ عام، فإن الكائن العالمي لديه طريقة ماكياد ().

تعتقد أن الهيئات دون عالم منطقية؟ إذا كان الأمر كذلك، فربما تجد هو أن بعض مجموعة فرعية من أساليب الجسم قد تكون مفيدة بدون عالم، ولست من الواضح كيف تنفذها - بوضوح لا يمكن تنفيذها بواسطة B2body، لأنه لا يمكن أن يكون موجودا بدون B2World وبعد حتى احتمال واحد هو أن لديك مجموعة من معلومات التكوين

 class Body {
        int howBig;
        String name;
        Flavour flavour;
        // and getter/setters
 } 

الآن يمكن أن تكون هذه (أو في شرق البكرات) بوضوح منطقية مع أو بدون عالم.

مع وضع ذلك في الاعتبار، أعتقد أنك قد تجد أنك حقيقي لديك اثنين من "دول" للجسم، واحد عندما لا يرتبط مع العالم، واحد عندما يكون. والقدرات الفعلية هي مختلف. وبعد وبالتالي لديك بالفعل اثنين من واجهات.

لذلك لديك فئة مستقلة وفئة الجسم. قد يكون طريقة مصنع العالم توقيع

World {

    Body makeBody(IndependentBody);

}

بعد الرابط الخاص بك، أرى ذلك createBody لا يعيد B2body، ولكن مؤشر إلى واحد:

 b2Body* b2World::CreateBody  ( const b2BodyDef*  def );     

هذا من المرجح أن B2World

  1. يدير حياة B2body (بمعنى آخر, ، حذفها والذاكرة التي يستخدمها عند الخروج من نطاق B2World من النطاق / هو نفسه المحذوف)، أو

  2. لأن B2WSorld يحتاج إلى الحفاظ على مؤشرات إلى B2Bodies، على سبيل المثال للتأكيد عليها لإنجاز بعض وظيفة B2World.

أذكر أيضا كل ما هو مطلوب (بخلاف b2World) لخلق b2Body هو مؤشر إلى b2BodyDef.

لذلك إذا كنت تريد B2Body غير متصل ب B2World، ولكن في وقت لاحق يتم إرفاقها لاحقا، فلماذا لا تمر حول B2BodeDef أو مؤشرات لهم؟

أنا ربما قم بإنشاء مجمع رقيق ل B2BodyDef، على سبيل المثال,:

 class b2BodyDefWrapper {
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reifyOn( b2World& world) const { 
     return world.CreateBody( b2bodyDef ) ;
   }
 }

لاحظ أنه يمكنني إرفاق هذا b2bodydefwrapper إلى عوالم متعددة، أو لنفس العالم أكثر من مرة.

الآن قد يكون ذلك أنه يمكنك القيام بأشياء B2body التي لا يمكنك القيام بها إلى B2BodyDef، وذلك بعد أن تناسب B2BodeDef (ربما ملفوفة) B2DyDEFS لأغراضك. في هذه الحالة، قد أستخدم نمط الأمر إلى "إرفاق" قائمة الوظائف على b2BodyDefWrapper, ، من شأنها أن "إعادة التشغيل" على كل b2body التي تم تجديدها:

 class b2BodyDefWrapper {
   private std::vector<Command&> commandStack;
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reify( b2World& world) const { 
     b2body* ret = world.CreateBody( &b2bodyDef ) ;
     for (int i=0; i< commandStack.size(); i++) {
        v[i].applyTo( ret ) ;
     }
     return ret;
   }

   public void addCommand( const Command& command ) {
      commandStack.push_back( command );
   }
 }

أين Command هي فئة قاعدة مجردة للمدافين، مثل هذا:

  class Command {
     virtual ~Command() {}
     virtual void applyTo( b2Body* body ) = 0 ;
  }

مع فرعية ملموسة:

 class ApplyForce : public Command {
   private const b2Vec2& force;
   private const b2Vec2& point;
   ApplyForce(const b2Vec2& f, const b2Vec2& p) : force(f), point(p) {}
   virtual void applyTo( b2Body* body ) {
      body->ApplyForce( force, point ) ;
   }
 }

ثم يمكنني استخدام غلاف بلدي مثل هذا:

extern b2BodyDef& makeb2BodyDef();
b2BodyDefWrapper w( makeb2BodyDef()  ) ; 
ApplyForce a( ..., ... );
w.addCommand( a ) ;
...
b2World myworld;
b2World hisWorld;
w.reifyOn( myWorld ) ;
w.reifyOn( hisWorld) ;

لاحظ أنني تركت بعض التفاصيل، بشكل أساسي حول ملكية الكائنات وإدارة الذاكرة، والذين يستدعي الحذف على CommandStacks؛ أنا أيضا لم تابع القاعدة الثالثة في رسوماتي للفصول. يمكنك ملء هذه هذه كما تريد.

كما أنني قد تركت أي حكم للاتصال، من أمر، B2body يعمل بخلاف باطلة وإرجاع هذه القيم؛ ربما يمكنك تغطية هذا (يجب أن تحتاج إلى) من خلال تطبيق إرجاع نقابة من نوع ما.

أكثر من الأساسي، لم أغطي كيف يمكن لأحد قيادة ملموسة توفير قيمة الإرجاع (إن وجدت) أمر ملموس آخر. سيكون الحل الكامل هو عدم وجود ناقلات من الأوامر، ولكن ن- شجرة منهم، حيث يتم تطبيق أوامر الأطفال أولا، وتزويد قيم المرتجعة (إن وجدت) الأمر الأمر الوالد. سواء كنت يحتاج مثل هذا التعقيد هو سؤال من الواضح أنه لا يمكنني الإجابة عليه. (ولقد أعطيت بالفعل إجابة مفصلة للغاية، اعتبرت ألا أجرت مقابل هذا الأمر، ولا يمكنني الحصول على نقطة سمعة، لأنك مجتمع Wiki'd هذا السؤال.)

أوافق على أنه لا ينبغي أن تقاتل تصميم مكتبة الطرف الثالث الذي تستخدمه. التوجه إلى أسفل هذا المسار يمكن أن يسبب الكثير من المشاكل في المستقبل.

من خلال البحث عن "تحت الأغطية" وإنشاء الأغلفة، قد تكون قفل سلوك مكتبة الطرف الثالث إلى الطريقة التي يتصرف بها التنفيذ الحالي.

ماذا سيحدث إذا بقيت نسخة مستقبلة من واجهة برمجة التطبيقات نفسها، ولكن تغيير الدلالات الأساسية؟

فجأة، يتم كسر كل شيء من وجهة نظر التفاف الخاص بك.

فقط بلدي 0.02.

أحد الأسباب التي يستخدم box2d كائنات bodydef لإنشاء كائنات B2body هو بحيث يمكنك إعادة استخدام DEF لإنشاء جثث متعددة. رمز مثل:

b2BodyDef myDef;
// fill out def

for (int i=0; i < 100; ++i) {
   for (int j=0; j < 100; ++j) {
      myDef.x = i;
      myDef.y = j
      b2Body* body = world.CreateBody(myDef)
   }
}

هي طريقة فعالة للغاية ومضغطة لإنشاء العديد من الأشياء بنفس الخصائص. لا يجب أن يكون في نفس الحلقة سواء، يمكنك الحفاظ على كائنات DEF حول البيانات الوصفية، وإنشاء جثث منها حسب الحاجة.

لا تقاتلها لأنها هناك لسبب ما.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top