Альтернатива наследованию интерфейса с Джинни - PullRequest
0 голосов
/ 29 апреля 2018

Я использую Djinni, чтобы поделиться большой базой кода на c ++ между android и ios. Один из различных компонентов (назовем его Foo !!!) имеет различную реализацию на android и ios. Foo - это интерфейс, который используется кодом C ++ без необходимости что-либо знать о его внутренностях.

В реализации Android (FooAndroid) есть несколько дополнительных методов, которые клиент Android может использовать для изменения поведения компонента способами, имеющими смысл только для платформы Android.

Это усложняет работу с джиннами из-за отсутствия наследования интерфейса. Я придумал своего рода решение, основанное на том факте, что FooAndroid может создавать подклассы двух разных интерфейсов djinni, большинство методов которых имеют одинаковую сигнатуру, но результат не очень красивый.

Это описание интерфейса джиннов:

foo = interface +c {
    static create() : foo;
    doSomething();
}

foo_android = interface +c {
    static create() : foo_android;
    doSomething();
    doAnotherThing();
    asFoo(): foo;
}

bar = interface +c {
    static create() : bar;
    useFoo(f: foo);
}

Вы заметите Bar компонент, которому просто необходим доступ к экземпляру Foo.

Полученные интерфейсы затем реализуются следующим образом:

// Foo android implementation:

class FooAndroidImpl : public Foo, public FooAndroid {
public:
    void doSomething() override { LOGI("do something"); }
    void doAnotherThing() override { LOGI(" do something"); }

    std::weak_ptr<Api::Foo> fooSelfReference;

    std::shared_ptr<Api::Foo> asFoo() override { return fooSelfRef.lock(); }

};

// instance creation methods:

std::shared_ptr<FooAndroidImpl> createImpl() {
    auto p = std::make_shared<FooAndroidImpl>();
    p->fooSelfRef = p;
    return p;
}

std::shared_ptr<FooAndroid> FooAndroid::create()
{
    return createImpl();
}

std::shared_ptr<Foo> Foo::create()
{
    return createImpl();
}

// Bar implementation:

class BarImpl : public Bar {
public:
    void useFoo(const std::shared_ptr<Foo> & f) override { f->doSomething(); }
};

std::shared_ptr<Bar> Bar::create() 
{ 
    return std::make_shared<BarImpl>();
}

Затем я могу использовать FooAndroid и Bar в Java следующим образом:

FooAndroid fooAndroid = FooAndroid.create();
fooAndroid.doAnotherThing();
Bar bar = Bar.create();
bar.useFoo(fooAndroid.asFoo());

Это работает, но это уродливо, мне приходится вручную определять два почти идентичных интерфейса djinni, и я до сих пор не уверен во всех последствиях сохранения этого weak_ptr для жизненного цикла объекта.

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

1 Ответ

0 голосов
/ 10 мая 2018

Джинни не поддерживает наследование интерфейса, поэтому здесь нет хорошего ответа. В Dropbox это происходит не часто, и мы обычно решаем эту проблему, просто добавляя методы, специфичные для Android / iOS, в единый интерфейс и реализуя их с заглушками, которые выдают исключение на другой платформе. Мы обычно сталкиваемся с обратным случаем, где реализация была в Java / ObjC.

Если вам не нравится этот подход, и вам не нравится дублирование методов и множественное наследование, я думаю, что другой подход, который я бы порекомендовал, состоял бы в использовании связанных подобъектов. Например. иметь интерфейс Foo с методом getAndroidStuff (), который возвращает интерфейс FooAndroid, содержащий только методы, специфичные для Android. Объект FooAndroid может внутренне удерживать ссылку обратно на объект FooImpl, который обладает всеми функциями, и перенаправлять на него методы.

Кажется, что более широкий вопрос здесь заключается в том, что на самом деле зависит от платформы в этих методах? Если реализация является кроссплатформенной, но требуется только на одной платформе, то не мешает просто использовать один класс C ++, а неиспользуемые методы не опасны. Если у вас есть платформо-зависимый код на C ++, то все становится немного неловко, так как это не было целью Djinni. Я думаю, что этот конкретный случай требует решения, отличного от общего случая наследования интерфейса.

...