C ++ / Java Наследование против Делегирования против и т. Д. - PullRequest
1 голос
/ 08 апреля 2009

Я создаю библиотеку классов с множеством различных опций для возможных настроек. Например, вы можете спроектировать свой класс так, чтобы он мог выполнять FeatureX (), или вы можете спроектировать свой класс так, чтобы он мог выполнять FeatureY ().

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

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

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

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

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

Спасибо.

Ответы [ 7 ]

2 голосов
/ 08 апреля 2009

Если вы не боитесь использовать шаблоны, вы можете сделать свою функцию шаблоном и использовать 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, расширяющий оба, и использовать это в параметрах функции; это имеет тот недостаток, что типы, которые делают реализуют оба интерфейса, но не этот совместный интерфейс не будет использоваться с этим методом.

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

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

2 голосов
/ 08 апреля 2009

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

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

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

1 голос
/ 08 апреля 2009

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

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

0 голосов
/ 08 апреля 2009

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

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
0 голосов
/ 08 апреля 2009

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 голосов
/ 08 апреля 2009

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

0 голосов
/ 08 апреля 2009

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

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