Как создать публичный API для существующей кодовой базы? - PullRequest
3 голосов
/ 15 декабря 2010

Я пытаюсь создать общедоступный API C ++ для частной базы кода, которая также написана на C ++.Я попытался сделать это следующим образом:

  • Создать класс API-оболочки для каждого частного класса.
  • Иметь общий базовый класс API, который имеет указатель на закрытый класс.
  • Реализации класса API не содержат никакой функциональности, а просто перенаправляют вызовы методов их частным аналогам.

Это казалось разумным подходом.Однако в действительности это быстро приводит к очень неуклюжему коду.Вот пример, который представляет проблему, с которой я столкнулся.

(Изменить: код также можно просмотреть здесь ).


частные классы API (здесь нет проблем)

namespace Core {


// Base class for all Core classes.
class CoreObject
{
public:
    virtual ~CoreObject();
};    

class Interface;
class Stream;    

class Server : public CoreObject
{
public:
    Interface * createInterface();

private:
    std::vector<Interface*> mInterfaces;

};    

class Interface : public CoreObject
{
public:
    void addStream(Stream * stream);

    const Stream * getStreamByIndex(std::size_t index) const;

    std::size_t streamCount() const;

private:
    std::vector<Stream*> mStreams;
};    

class Stream : public CoreObject
{
public:
    void start();

    void stop();

private:
    std::string mStats;
};


} // Core


объявления классов открытого API (пока что все хорошо)

namespace Core {
class CoreObject;
}


namespace API {


class APIStream;
class APIInterface;    

// Base class for all API classes.
class APIObject
{
public:
    APIObject(Core::CoreObject * inCoreObject);

    virtual ~APIObject();

    Core::CoreObject * getCoreObject();

    const Core::CoreObject * getCoreObject() const;

    void setCoreObject(Core::CoreObject * inCoreObject);

private:
    Core::CoreObject * mCoreObject; 
};    

class APIServer : public APIObject
{
public:
    APIServer();

    APIInterface * createInterface();
};    

class APIInterface : public APIObject
{
public:
    APIInterface();

    void addStream(APIStream * stream);

    const APIStream * getStreamByIndex(std::size_t index) const;

    APIStream * getStreamByIndex(std::size_t index);

    std::size_t streamCount() const;
};    

class APIStream : public APIObject
{
public:
    APIStream();

    void start();

    void stop();
};


Реализация API (слишком много приведений, как правило, неуклюжий)

#include "API.h"
#include "Core.h"


namespace API {


APIObject::APIObject(Core::CoreObject * inCoreObject) :
    mCoreObject(inCoreObject)
{
}

APIObject::~APIObject()
{
}

Core::CoreObject * APIObject::getCoreObject()
{
    return mCoreObject;
}


const Core::CoreObject * APIObject::getCoreObject() const
{
    return mCoreObject;
}


void APIObject::setCoreObject(Core::CoreObject * inCoreObject)
{
    mCoreObject = inCoreObject;
}


//
// APIServer
//
APIServer::APIServer() :
    APIObject(new Core::Server)
{
}    

APIInterface * APIServer::createInterface()
{
    Core::Server * coreServer = static_cast<Core::Server*>(getCoreObject());
    Core::Interface * coreInterface = coreServer->createInterface();
    APIInterface * result(new API::APIInterface);
    result->setCoreObject(coreInterface);
    return result;
}


//
// APIInterface
//
APIInterface::APIInterface() :
    APIObject(new Core::Interface)
{
}    

void APIInterface::addStream(APIStream * apiStream)
{
    Core::Stream * coreStream = static_cast<Core::Stream *>(apiStream->getCoreObject());
    Core::Interface * coreInterface = static_cast<Core::Interface*>(getCoreObject());
    coreInterface->addStream(coreStream);
}


//
// APIStream
//
const APIStream * APIInterface::getStreamByIndex(std::size_t index) const
{
    const Core::Interface * coreInterface = static_cast<const Core::Interface*>(getCoreObject());
    const Core::Stream * coreStream = coreInterface->getStreamByIndex(index);

    // Now how I get the the APIStream object?
    return 0;
}

std::size_t APIInterface::streamCount() const
{
    const Core::Interface * coreInterface = static_cast<const Core::Interface*>(getCoreObject());
    return coreInterface->streamCount();
}    

APIStream::APIStream() :
    APIObject(new Core::Stream)
{
}    

void APIStream::start()
{
    static_cast<Core::Stream*>(getCoreObject())->start();
}       

void APIStream::stop()
{
    static_cast<Core::Stream*>(getCoreObject())->stop();
}    

} // API


Как видите, реализация выглядит не слишком хорошо.Буду признателен за ваши ответы или советы по этим вопросам:

  • Где я ошибся?
  • Что я должен был сделать вместо этого?


Обновление

Предложение Джона Диблинга, похоже, работает действительно хорошо.Как видно из улучшенного кода , все проблемы решаются аккуратно.

Мне все еще придется применить это решение к реальному коду на работе завтра.Мне интересно посмотреть, насколько хорошо он там работает.

Ответы [ 2 ]

4 голосов
/ 15 декабря 2010

При выполнении такого рода вещей, когда между API и объектами реализации существует более или менее однозначное отношение, я обычно использую статический фабричный метод и получаю реализацию класс происходит от класса API. Как это:

файл: api.h

class Interface
{
public:
  static Interface* Create();
  virtual void Foo() = 0;
};

файл: impl.h

class Concrete : public Interface
{
public:
  void Foo() {};
};

файл: impl.cpp

Interface* Interface::Create()
{
  return new Concrete;
}

Это имеет ряд преимуществ. В том числе:

  1. Create может создавать множество различных реализаций, и ABI Interface не нужно менять.
  2. Типы возврата основаны на правилах ковариации языка, поэтому приведение не требуется.
  3. Отладка несколько проще, потому что вы можете точно сказать, какой у вас Interface.
2 голосов
/ 15 декабря 2010

См. Адаптер Шаблон

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...