Необязательные объекты-члены - PullRequest
2 голосов
/ 12 июня 2010

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

class Feature
{
public:
    Feature(){};

    void doSomething();
    void doSomething1();
    void doSomething2();   
};

Так что теперь у вашего исходного класса есть переменная-член типа object:

  Feature _feature;

Что вы и сделаетепозвонить в основной класс.Теперь, если вы будете делать это много раз, у вас будет много объектов-членов в вашем основном классе.

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

Кто-нибудь может предложить способ улучшить это?


РЕДАКТИРОВАТЬ: Основано на предложении использовать шаблон проектирования пустых объектов, я придумал это:

Абстрактный класс, определяющий интерфейс объекта:

class IFeature
{
public:
    virtual void doSomething()=0;
    virtual void doSomething1()=0;
    virtual void doSomething2()=0;

    virtual ~IFeature(){}
};

Затем я определяю два класса, которые реализуют интерфейс, одну реальную реализацию и один нулевой объект:

class RealFeature:public IFeature
{
public:
    RealFeature(){};

    void doSomething(){std::cout<<"RealFeature doSomething()"<<std::endl;}
    void doSomething1(){std::cout<<"RealFeature doSomething()"<<std::endl;}
    void doSomething2(){std::cout<<"RealFeature doSomething()"<<std::endl;}
}; 

class NullFeature:public IFeature
{
public:
    NullFeature(){};

    void doSomething(){std::cout<<"NULL doSomething()"<<std::endl;};
    void doSomething1(){std::cout<<"NULL doSomething1()"<<std::endl;};
    void doSomething2(){std::cout<<"NULL doSomething2()"<<std::endl;};


};

Затем я определяю класс Proxy, который будет делегировать либо реальному объекту, либо нулевому объекту в зависимости от конфигурации:

class Feature:public IFeature
{
public:
    Feature();
    ~Feature();

    void doSomething();
    void doSomething1();
    void doSomething2();

private:
    std::auto_ptr<IFeature> _feature;
};

Реализация:

   Feature::Feature()
    {
        std::cout<<"Feature() CTOR"<<std::endl;
        if(configuration::isEnabled() )
        {
            _feature = auto_ptr<IFeature>( new RealFeature() );
        }
        else
        {
            _feature = auto_ptr<IFeature>( new NullFeature() );
        }
    }


    void Feature::doSomething()
    {
        _feature->doSomething();
    }

    //And so one for each of the implementation methods

Затем Iиспользуйте прокси-класс в моем основном классе (или там, где это требуется):

Feature _feature;

_feature.doSomething();

Ответы [ 4 ]

3 голосов
/ 12 июня 2010

Авто_птр сам по себе не купит вам много.Но иметь указатель на объект, который вы лениво загружаете, только когда и если вам это нужно, может.Что-то вроде:

class Foo {
private:
    Feature* _feature;
public:
    Foo() : _feature(NULL) {}
    Feature* getFeature() {
        if (! _feature) {
            _feature = new Feature();
        }
        return _feature;
    }
};

Теперь вы можете обернуть это Feature* в умный указатель, если вам нужна помощь с управлением памятью.Но ключ не в управлении памятью, а в ленивом творении.Преимущество этого вместо выборочной настройки того, что вы хотите создать во время запуска, заключается в том, что вам не нужно настраивать - вы просто платите по ходу дела.Иногда это все, что вам нужно.

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

3 голосов
/ 12 июня 2010

Если какая-либо функция отсутствует и правильное решение - игнорировать этот факт и ничего не делать, вы можете избавиться от своих проверок, используя шаблон Null Object :

class MainThing {
    IFeature _feature;

    void DoStuff() {
        _feature.Method1();
        _feature.Method2();
}

interface IFeature {
    void Method1();
    void Method2();
}

class SomeFeature { /* ... */ }

class NullFeature {
    void Method1() { /* do nothing */ }
    void Method2() { /* do nothing */ }
}

Теперь, в MainThing, если дополнительная функция отсутствует, вы даете ей ссылку на NullFeature вместо фактической null ссылки.Таким образом, MainThing всегда может с уверенностью предположить, что _feature не null.

1 голос
/ 17 июня 2010

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

Конкретно, вы можете иметь:

class FeatureImpl
{
public:
    void doSomething() { /*real work here*/ }
};

class Feature
{
    class FeatureImpl * _impl;
public:
    Feature() : _impl(0) {}
    void doSomething()
    {
        if(_impl)
            _impl->doSomething();
        // else case ... here's your null object implementation :)
    }
    // code to (optionally) initialize the implementation left out due to laziness
};

Этот код получает выгоду от реализации NULL только в том случае, если он критичен к производительности (и даже в этом случае стоимость if(_impl) в большинстве случаев незначительна).

1 голос
/ 12 июня 2010

В вашем описании проблемы есть один момент, который фактически может привести к сбою. Вы не должны «просто возвращать» , если ваша функция недоступна, вы должны проверить доступность вашей функции, прежде чем вызывать ее!

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

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

Также следует отметить, что эта подпрограмма feature-lookup должна быть быстрой (я так думаю) и не повлияет на вашу производительность.


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

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