كيف يمكنني تتبع (التعداد) جميع الطبقات التي تنفذ واجهة

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

  •  12-09-2019
  •  | 
  •  

سؤال

لدي موقف حيث لدي واجهة تحدد كيف يتصرف فئة معينة من أجل ملء دور معين في برنامجي، ولكن في هذه المرحلة الزمنية أنا لست متأكدا 100٪ عدد الفصول التي سأكتبها لملء هذا الدور وبعد ومع ذلك، في الوقت نفسه، أعرف أنني أريد أن يكون المستخدم قادرا على التحديد، من مربع كومبو / قائمة GUI، أي فئة ملموسة تنفذ الواجهة التي يريدون استخدامها لملء دور معين. أريد أن يكون واجهة المستخدم الرسومية قادرا على تعداد جميع الفئات المتاحة، لكنني أفضل عدم الرجوع إلى العودة وتغيير الرمز القديم كلما قررت تطبيق فئة جديدة لملء هذا الدور (الذي قد يكون أشهر من الآن)

بعض الأشياء التي نظرت فيها:

  1. باستخدام تعداد
    • الايجابيات:
      1. أنا أعرف كيفية القيام بذلك
    • سلبيات
      1. سيتعين علي تحديث تحديث التعداد عند إضافة فئة جديدة
      2. قبيح للتكرار من خلال
  2. باستخدام نوع من static اذكر كائن في الواجهة، وإضافة عنصر جديد من داخل ملف تعريف الفئة المنفذة
    • الايجابيات:
      1. لن تضطر إلى تغيير الرمز القديم
    • سلبيات:
      1. ليس بالتأكيد إذا كان هذا ممكن
      2. لست متأكدا من نوع المعلومات لتخزينها بحيث يمكن لطريقة المصنع اختيار المنشئ الصحيح (ربما خريطة بين سلسلة ومؤشر دالة تعرض مؤشر إلى كائن من الواجهة)

أنا أظمني أن هذه مشكلة (أو مشابهة للمشكلة) التي ربما صادفها مبرمجون أكثر خبرة قبل (وغالبا)، ومن المحتمل أن يكون هناك حل مشترك لهذا النوع من المشكلات، وهو أمر أفضل بالتأكيد أفضل من أي شيء "م قادرة على الخروج مع. لذلك، كيف أفعل ذلك؟

(PS لقد بحثت، ولكن كل ما وجدته كان هذا، وليس هو نفسه: كيف يمكنني تعداد جميع العناصر التي تنفذ واجهة عامة؟. وبعد يبدو أنه يعرف بالفعل كيفية حل المشكلة التي أحاول معرفةها.)

تحرير: قمت بإعادة تسمية العنوان "كيف يمكنني تتبع ..." بدلا من مجرد "كيف يمكنني التعداد ..." لأن السؤال الأصلي بدا كما كنت مهتما في فحص بيئة وقت التشغيل، حيث ما أنا عليه "م مهتما حقا هو كومبليز لحفظ الكتب.

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

المحلول

قم بإنشاء Singleton حيث يمكنك تسجيل فصولك بمؤشر إلى وظيفة الخالق. في ملفات CPP من فصول الخرسانة التي تقوم بتسجيل كل فئة.
شيء من هذا القبيل:

class Interface;
typedef boost::function<Interface* ()> Creator;

class InterfaceRegistration
{
    typedef map<string, Creator> CreatorMap;
public:
    InterfaceRegistration& instance() {  
        static InterfaceRegistration interfaceRegistration;
        return interfaceRegistration;
    }

    bool registerInterface( const string& name, Creator creator )
    {
        return (m_interfaces[name] = creator);
    }

    list<string> names() const
    {
        list<string> nameList;  
        transform(
            m_interfaces.begin(), m_interfaces.end(), 
            back_inserter(nameList) 
            select1st<CreatorMap>::value_type>() );
    }

    Interface* create(cosnt string& name ) const 
    { 
        const CreatorMap::const_iterator it 
            = m_interfaces.find(name);  
        if( it!=m_interfaces.end() && (*it) )
        {
            return (*it)();
        }
        // throw exception ...
        return 0;
    }

private:
    CreatorMap m_interfaces;
};


// in your concrete classes cpp files
namespace {
bool registerClassX = InterfaceRegistration::instance("ClassX", boost::lambda::new_ptr<ClassX>() );
}

ClassX::ClassX() : Interface()
{
    //....
}

// in your concrete class Y cpp files
namespace {
bool registerClassY = InterfaceRegistration::instance("ClassY", boost::lambda::new_ptr<ClassY>() );
}

ClassY::ClassY() : Interface()
{
    //....
}

نصائح أخرى

أتذكر غامضة القيام بشيء مشابه منذ عدة سنوات. الخيار الخاص بك (2) هو ما فعلته إلى حد كبير. في هذه الحالة كان std::map من std::string ل std::typeinfo. وبعد في كل ملف .cpp قمت بتسجيل الفئة مثل هذا:

static dummy = registerClass (typeid (MyNewClass));

registerClass يأخذ type_info كائن وعودة ببساطة true. وبعد يجب عليك تهيئة متغير لضمان ذلك registerClass يسمى خلال وقت بدء التشغيل. ببساطة الاتصال registerClass في مساحة الاسم العالمية خطأ. وصنع dummy يسمح لك Satic بإعادة استخدام الاسم عبر وحدات الترجمة دون تصادم اسم.

أشارت إلى هذه المقالة لتنفيذ مصنع من فئة التسجيل الذاتي مماثلة للمجموعة الموضحة في إجابة TITW، ولكن لديها خدعة جميلة من استخدام فئة وكيل المصنع القالب للتعامل مع تسجيل الكائنات. حسنا يستحق نظرة :)

الكائنات المسجلة ذاتية في C ++ -> http://www.ddj.com/184410633.

يحرر

إليك تطبيق الاختبار الذي قمت به (رتب قليلا؛):

Object_Factory.h.

#include <string>
#include <vector>
// Forward declare the base object class
class Object;
// Interface that the factory uses to communicate with the object proxies
class IObjectProxy {
public:
    virtual Object* CreateObject() = 0;
    virtual std::string GetObjectInfo() = 0;
};
// Object factory, retrieves object info from the global proxy objects
class ObjectFactory {
public:
    static ObjectFactory& Instance() {
        static ObjectFactory instance;
        return instance;
    }
    // proxies add themselves to the factory here 
    void AddObject(IObjectProxy* object) {
        objects_.push_back(object);
    }
    size_t NumberOfObjects() {
        return objects_.size();
    }
    Object* CreateObject(size_t index) {
        return objects_[index]->CreateObject();
    }
    std::string GetObjectInfo(size_t index) {
        return objects_[index]->GetObjectInfo();
    }

private:
    std::vector<IObjectProxy*> objects_;
};

// This is the factory proxy template class
template<typename T>
class ObjectProxy : public IObjectProxy {
public:
    ObjectProxy() {
        ObjectFactory::Instance().AddObject(this);
    }        
    Object* CreateObject() {
        return new T;
    }
    virtual std::string GetObjectInfo() {
        return T::TalkToMe();
    };    
};

الموضوعات .h.

#include <iostream>
#include "object_factory.h"
// Base object class
class Object {
public:
    virtual ~Object() {}
};
class ClassA : public Object {
public:
    ClassA() { std::cout << "ClassA Constructor" << std::endl; }
    ~ClassA() { std::cout << "ClassA Destructor" << std::endl; }
    static std::string TalkToMe() { return "This is ClassA"; }
};
class ClassB : public Object {
public:
    ClassB() { std::cout << "ClassB Constructor" << std::endl; }
    ~ClassB() { std::cout << "ClassB Destructor" << std::endl; }
    static std::string TalkToMe() { return "This is ClassB"; }
};

outss.cpp.

#include "objects.h"
// Objects get registered here
ObjectProxy<ClassA> gClassAProxy;
ObjectProxy<ClassB> gClassBProxy;

main.cpp.

#include "objects.h"
int main (int argc, char * const argv[]) {
    ObjectFactory& factory = ObjectFactory::Instance();
    for (int i = 0; i < factory.NumberOfObjects(); ++i) {
        std::cout << factory.GetObjectInfo(i) << std::endl;
        Object* object = factory.CreateObject(i);
        delete object;
    }
    return 0;
}

انتاج:

This is ClassA
ClassA Constructor
ClassA Destructor
This is ClassB
ClassB Constructor
ClassB Destructor

إذا كنت على Windows، واستخدام C ++ / CLI، يصبح هذا سهلا إلى حد ما. يوفر .NET Framework هذه القدرة عبر التفكير، ويعمل نظيفة للغاية في التعليمات البرمجية المدارة.

في Native C ++، يحصل هذا على مستوى بسيط قليلا، حيث لا توجد طريقة بسيطة للاستعلام عن المكتبة أو التطبيق لمعلومات وقت التشغيل. هناك الكثير الأطر التي توفر هذا (ابحث عن أطر IOC أو DI أو Plugin)، ولكن أبسط وسيلة للقيام بذلك بنفسك هي أن يكون لها شكل من أشكال التكوين التي يمكن أن تستخدمها طريقة المصنع لتسجيل أنفسهم وإرجاع تنفيذ فئة الأساس الخاصة بك. تحتاج فقط إلى تنفيذ تحميل DLL، وتسجيل طريقة المصنع - بمجرد أن يكون لديك ذلك، إنه أمر سهل إلى حد ما.

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

طريقة أنيقة للقيام بذلك هي استخدام CRTP: نمط قالب متكرر الغريبوبعد المثال الرئيسي هو مثل هذا العداد :)

بهذه الطريقة يجب عليك فقط إضافةها في تنفيذ فئة ملموسة:

class X; // your interface

class MyConcreteX : public counter<X>
{
    // whatever
};

بالطبع، لا ينطبق إذا كنت تستخدم التطبيقات الخارجية التي لا تتقنها.

تعديل:

للتعامل مع المشكلة الدقيقة التي تحتاج إلى وجود عداد يحسب فقط المثيل الأول.

بلدي 2 سنتا

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

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