Существует ли способ запретить разделение на подклассы моего класса?

Если метод ничего не возвращает, это - любой один из следующих

  • императив - Вы или спрашиваете объект сделать что-то к себе.. например, состояние изменения (не ожидая подтверждения.. его принятый, что это будет сделано)
  • информационный - просто уведомление кого-то, что что-то произошло (не ожидая действие или ответ) соответственно.

Обязательные методы - можно проверить, была ли задача на самом деле выполнена. Проверьте, произошло ли изменение состояния на самом деле. например,

void DeductFromBalance( dAmount ) 

может быть протестирован путем проверки, добавляет ли баланс это сообщение, действительно меньше, чем начальное значение dAmount

, Информационные методы - редки как интерфейс представителя общественности объекта... следовательно не обычно протестированного на единицу. Однако, если Вы должны, можно проверить, происходит ли обработка, которая будет сделана на уведомлении. например,

void OnAccountDebit( dAmount )  // emails account holder with info

может быть протестирован путем проверки, посылается ли электронное письмо

Сообщение, больше деталей о фактическом методе и людях будет в состоянии ответить лучше.
Обновление : Ваш метод делает 2 вещи. Я на самом деле разделил его на два метода, которые могут теперь быть независимо протестированы.

string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );

Строка [] может быть легко проверена путем предоставления первому методу с фиктивным файлом и ожидала строки. Второй немного хитер.. можно или использовать Насмешку (Google или искать stackoverflow при насмешке платформ) подражать DB или поражать фактический DB и проверить, были ли строки вставлены в правильное местоположение. Проверьте этот поток для некоторых хороших книг... Я повторно прокомментировал бы Прагматическое Поблочное тестирование, если Вы находитесь в уплотнении.
В коде это использовалось бы как [1 116]

InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );

16
задан Lightness Races with Monica 26 December 2011 в 13:17
поделиться

4 ответа

У вас может быть частный конструктор для ' Derived ' и общедоступная статическая функция Create для создания экземпляра

9
ответ дан 30 November 2019 в 17:39
поделиться

Самый простой способ запретить создание подклассов - сделать конструктор закрытым:

class Foo
{
private:
    Foo() {}

public:
    static Foo* CreateFoo() { return new Foo; }
};

Редактировать: Спасибо Indeera за указание, что для этого нужен статический метод Factory

5
ответ дан 30 November 2019 в 17:39
поделиться

Нет простого и чистого способа сделать это.

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

В конечном итоге, вам нужен сделать создание подкласса невозможным ? Разве не достаточно указать, что «создание производных от этого класса - плохая идея»?

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

Защитите свой код от Мерфи, а не от Макиавелли. ;)

3
ответ дан 30 November 2019 в 17:39
поделиться

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

Следующий код snippet - это то, что я придумал, но требуемая сложность только усиливает ответ jalf. Стоит ли оно того? Если что-то это помогло мне понять частичную специализацию больше, чем разработать технику, которую я когда-либо использовал бы на практике.

Я использую COMMON, чтобы указать общий параметр шаблона между Base и Derived и EXTRA, чтобы обозначить дополнительные параметры, которые, как вы говорите, Derived имеет . Фактическое их количество может быть любым, что я только что выбрал для них, соответственно, один и два.

// Forward declaration of class Derived
template< class COMMON
        , class EXTRA1
        , class EXTRA2 >
class Derived;


// Definition of general class template Base
template< class SUBCLASS
        , class COMMON >
class Base
{
private:
    Base() {}
};


// Definition of partial specialisation of template class Base to open up
// access to the constructor through friend declaration.
template< class COMMON
        , class EXTRA1
        , class EXTRA2 >
class Base< Derived< COMMON, EXTRA1, EXTRA2 >
          , COMMON >
{
private:
    Base() {}

    friend class Derived< COMMON, EXTRA1, EXTRA2 >;
};


// Definition of class Derived
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class Derived
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    static Derived* create() { return new Derived; }

private:
    Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                    , COMMON >()
    {
    }
};


// Definition of class HonestDerived.
// It supplies itself as the SUBCLASS parameter to Base.
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class HonestDerived
    : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                          , COMMON >()
    {
    }
};


// Definition of class DishonestDerived
// It supplies Derived rather than itself as the SUBCLASS parameter to Base.
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class DishonestDerived
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                             , COMMON >()
    {
    }
};


template< class COMMON, class EXTRA1, class EXTRA2 >
class DerivedFromDerived
    : public Derived< COMMON, EXTRA1, EXTRA2 >
{
public:
    DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >()
    {
    }
};

// Test partial specialisation gives Derived access to the Base constructor
Derived< int, float, double >* derived
    = Derived< int, float, double >::create();

// Test that there is no access to the Base constructor for an honest subclass
// i.e. this gives a compiler error
HonestDerived< int, float, double > honestDerived;

// Test that there is no access to the Base constructor for a dishonest subclass
// i.e. this gives a compiler error
DishonestDerived< int, float, double > dishonestDerived;

// Test that there is no access to the Derived constructor
// i.e. this gives a compiler error
DerivedFromDerived< int, float, double > derivedFromDerived;

Этот код был протестирован с gcc 4.3.2.

1
ответ дан 30 November 2019 в 17:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: