2009-04-07 2 views
1

Я создаю библиотеку классов со множеством различных параметров для возможных настроек. Например, вы можете создать свой класс, чтобы он мог выполнять FeatureX(), или вы можете создать свой класс, чтобы он мог выполнять функцию FeatureY().C++/Java Inheritance vs. Delegation vs. etc

В нормальных условиях вы просто создадите интерфейс IFeatureX с помощью чистого виртуального метода FeatureX и другого интерфейса IFeatureY с помощью чистого виртуального метода, называемого FeatureY. Если класс имеет как FeatureX, так и FeatureY, он может наследовать от обоих, без проблем.

Моя проблема в том, что если функции/методу требуется объект, который может выполнять функции FeatureX() и FeatureY()? Как я могу выразить тип на языке C++, но ответ на Java также может помочь, чтобы обеспечить доступность как FeatureX, так и FeatureY?

Я могу создать другой интерфейс IFeatureXY, который наследуется от IFeatureX и IFeatureY? Хорошо ... если есть только две возможности, я мог бы с этим справиться. Но если есть сказать ... 10 функций, количество возможных интерфейсов становится массивным.

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

Я ценю любую помощь и советы, которые у вас есть.

Спасибо.

ответ

2

Если вы не боитесь использования шаблонов, вы можете сделать ваши функции шаблона и использовать SFINAE для проверки двух интерфейсов:

template <class T> 
void my_function(const T& data, typename enable_if_c< 
    is_convertible<T*, IFeatureX*>::value && 
    is_convertible<T*, IFeatureY*>::value>::type*=0) { 
    ... 
} 

Это создаст метод для каждого типа, который проходит как функцию интерфейсы (обратите внимание, что трюк SFINAE не нужен для его работы, а неконфиденциальный шаблон будет работать, но просто не скомпилируется, когда вы передаете тип, который не соответствует требованиям).

Другая возможность - создать интерфейс IFeatureXY, расширяющий оба параметра, и использовать его в параметрах функции; у этого есть недостаток, который вызывает то, что do реализует оба интерфейса, но не этот совместный интерфейс не будет использоваться с этим методом.

Кроме того, вы можете передать два аргумента функции, по одному на интерфейс, и потребовать, чтобы они указатели на один и тот же объект; это хрупкий, но может быть упрочнен, если создать класс шаблонов для хранения двух указателей - например. product_type<IFeatureX*, IFeatureY*>, который будет инициализирован одним объектом и будет содержать два типа.

В Java вы могли бы, вероятно, сделать то же самое с переменными ограниченного типа (если они допускают множественные границы, я не уверен сейчас).

+0

Спасибо, это похоже на то, что я имел в виду, но выглядит намного более надежным. – Kranar

2

Первое, что нужно сделать, это спросить, пытаетесь ли вы сделать что-то, что нельзя просто выразить, и если да, спросите себя, действительно ли это стоит делать?

Учитывая, что вы не можете найти более простую модель того, что вы хотите, вам нужно будет подумать о зависимостях между параметрами. Если вы можете использовать функцию X независимо от функции Y, тогда сделайте их независимыми интерфейсами или чистыми виртуальными классами (в зависимости от языка.)

Если вы не можете использовать их самостоятельно, создайте класс, который включает в себя оба; спросите себя, почему вы хотите FeatureX и FeatureY как отдельные интерфейсы, потому что эта модель использования предполагает, что они не являются независимыми в конце концов.

0

Если вы хотите, чтобы сделать это в C++, что относительно множественного наследования?

0

Возможно, вы слишком мелкозернистые. Рассмотрим числовой класс - вы можете выполнять умножение, деление, сложение, вычитание и т. Д. Однако вы не создавали бы отдельные интерфейсы для каждой из этих операций - вы бы создали один интерфейс под названием SupportsArithmetic (или что-то еще), который охватывал их все.

0

WCF имеет очень хороший шаблон, как определить, поддерживает ли какой-либо объект какой-либо интерфейс (или класс) с использованием IExtensionCollection<T>.Find<E>(). IExtensionCollection.

IFeatureX feature = argument.Find<IFeatureX>(); 

if (feature != null) 
{ 
    // Find() returned an instance so there is an implementation 
    // of IFeatureX available 

    feature.FeatureX(); 
} 

Таким образом, вы можете запросить свой объект для какой-либо цели. Аналогичный метод используется в COM + в IUnknown::QueryInterface().

0

Зачем нужны интерфейсы? Использование шаблонов:

template< typename T > 
void some_function(const T& t) 
{ 
    // use featureX functions 
    t.fetatureX(); 

    // use featureY functions 
    t.featureY(); 
} 

Использование:

SomeClass x; // object with only X feature 
some_function(x); // compile time error, because featureY() doesn't exists 
1

Хотя есть способы, чтобы добавить совершенно разнородные функции, вы можете думать о масштабах этих дополнительных функций. Они будут связаны с вашей основной библиотекой классов? (Можно было бы утверждать, что если это не так, они не должны быть его частью)

Если у них достаточно общего, чтобы гарантировать добавление функций, вы можете найти что-то вроде рисунка декоратора (http://en.wikipedia.org/wiki/Decorator_pattern). Это позволяет обойти некоторые из неудобных проблем с такими вещами.

Смежные вопросы