Какой подход к проектированию в C ++ более понятен? - PullRequest
4 голосов
/ 21 октября 2011

Текущие характеристики:

Данные строковые данные в виде широких или узких символьных массивов записывают функциональность для класса, который предоставляет статистику по данным и изменяет данные.

Требуется, чтобы он был ремонтопригодным в течение длительного времени.

Итак, мой первый подход заключается в том, чтобы исходные массивы символов маршалировались в строки, а затем просто предоставлялся шаблонный класс:

template<class T>
class MyString
{
private:
    T _data;
public:
    MyString(T& input) { _data = input; }; 
    size_t doSomeWork() { //assume T is of type basic_string<...> and use iterators };

};


//Use
const char* data = "zyx";
string blahblah(data);
MyString<string> abc(blahblah);
abc.doSomeWork();

или статические функции-члены:

class StringTools
{
public:
    static size_t doWork(const char*) {}
    static size_t doWork(const wchar_t*) {}
};

//used like so:
const char* data = "hallo kitty";
cout << StringTools::doWork(data);

или используйте шаблон стратегии:

class MyString
{
protected:
    MyStringBase();
public:
    virtual ~MyStringBase();    
    virtual size_t doWork() = 0;
};

class MyStringCharArray : public MyString
{
protected:
    char* _data;
public:
    MyStringCharArray(const char* input) : MyString() { }
    virtual size_t doWork() {...};
};

//so it would be used like so
const char* blah = "blah";
MyString* str = new MyStringCharArray(blah);
cout << str->doWork();
delete str;

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

Я думаю, что если я напишу класс-обёртку, как в 1 и 3, он станет намного более тяжелым и любая инкапсуляция будет нарушена, так как мне придется разрешить доступ к базовому объекту.

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

Так какой же подход будет самым чистым и обслуживаемым?

1024 * Rgds *

Ответы [ 4 ]

4 голосов
/ 21 октября 2011

Наилучшим подходом было бы сделать что-то вроде того, что делают алгоритмы stl.

Иметь алгоритм процесса, который принимает только строку char / wchart_t начала и конца итератора. Таким образом, ваш алгоритм будет бесперебойно работать со всеми строками, которые могут быть смежными в памяти.

1 голос
/ 21 октября 2011

Мне кажется, что вместо a класса вы должны думать об общих алгоритмах, которые работают с совершенно нормальными string / wstring данными. В зависимости от того, что вам нужно для статистики / модификации, вам может даже не понадобиться писать реальные алгоритмы, а просто использовать функторы (например) std::accumulate.

Если вам в конечном итоге понадобится работать с чем-то вроде BSTR, вам нужно будет предоставить интерфейс итератора для BSTR. Лично, однако, даже когда я имею дело с COM, я склонен фактически работать с обычными строками почти все время и преобразовывать в BSTR только непосредственно перед передачей его какой-то вещи COM. Аналогично, в обратном путешествии, как только я получаю BSTR, я преобразую его в обычную строку и работаю с ней в этой форме. Теоретически, было бы быстрее оставить все как BSTR, если вы немного работаете с COM, но я еще не видел, чтобы преобразование в / из обычных строк превратилось во что-то, приближающееся к узкому месту.

0 голосов
/ 22 октября 2011

Хорошо, я учел все, что вы сказали, и я отделил алгоритмы от спецификации. Имитируя STL, алгоритмы работают с итераторами; в то время как MyClass запрашивает спецификацию, инкапсулирует знание предметной области.

Как это выглядит для вас, ребята?

сначала отключите алгоритм:

/*
MyLib
---------------
Methods:
countOccurances()
    - given the iterator pairs for two either contiguous areas of memory or containers
    - counts the number of times the second (needle) occurs in the first (haystack)
    - returns the count

replaceOccurances()
    - same as countOccurances except when a sequence has been matched it is replaced 
      by the replacement needle which must be the same length

*/

template<class fwdIt>
size_t countOccurances(fwdIt haystackFront, fwdIt haystackEnd,
                              fwdIt needleFront, fwdIt needleEnd)
{   
    size_t lengthOfNeedle = std::distance(needleFront,needleEnd);
    size_t lengthOfHaystack = std::distance(haystackFront,haystackEnd);
    size_t count = 0;

    while(true)
    {
        //find the needle
        fwdIt tempIT1 = haystackFront, tempIT2 = needleFront;
        while(true)
        {
            if(tempIT2 == needleEnd)
            {
                haystackFront += lengthOfNeedle;
                lengthOfHaystack -= lengthOfNeedle;
                count++;
                break;
            }
            else if(*tempIT1 != *tempIT2)
            {
                break;
            }
            tempIT1++; tempIT2++;
        }
        if(lengthOfNeedle <= lengthOfHaystack)
        {
            ++haystackFront;
            --lengthOfHaystack;
        }
        else
        {
            break;
        }
    }
    return count;
}



template<class fwdIt>
size_t replaceOccurances(fwdIt haystackFront, fwdIt haystackEnd,
                              fwdIt needleFront, fwdIt needleEnd,
                              fwdIt replacementFront, fwdIt replacementEnd)
{
    //The needle and its replacement must be the same length, 
    //this method cannot be reponsible for growing a container it doesn't own.
    if(std::distance(needleFront, needleEnd) != std::distance(replacementFront, replacementEnd))
        throw exception("The needle and its replacement are not the same length");

    size_t lengthOfNeedle = std::distance(needleFront,needleEnd);
    size_t lengthOfHaystack = std::distance(haystackFront,haystackEnd);
    size_t count = 0;

    while(true)
    {
        //find the needle
        fwdIt tempIT1 = haystackFront, tempIT2 = needleFront;
        while(true)
        {
            if(tempIT2 == needleEnd)
            {
                //replace the needle
                for(fwdIt tempIT3 = replacementFront; 
                    haystackFront != tempIT1, tempIT3 != replacementEnd; 
                    haystackFront++, tempIT3++)
                {
                    *haystackFront = *tempIT3;
                }
                count++;
                break;
            }
            else if(*tempIT1 != *tempIT2)
            {
                break;
            }
            tempIT1++; tempIT2++;
        }
        if(lengthOfNeedle <= lengthOfHaystack)
        {
            ++haystackFront;
            --lengthOfHaystack;
        }
        else
        {
            break;
        }
    }
    return count;
}

и теперь MyClass

class MyClass
{
public:
    static size_t getMyCount(std::string& sInput);
    static size_t getMyCount(std::wstring& sInput);
    static size_t replaceMyWithMY(std::string& sInput);
    static size_t replaceMyWithMY(std::wstring& sInput);


protected:
    static std::string _narrowNeedle;
    static std::wstring _wideNeedle;
    static std::string _narrowReplacementNeedle;
    static std::wstring _wideReplacementNeedle;

    template<class T>
    static size_t _PerformStringOperation(T& sInput, T& sNeedle, bool replace = false, T& sReplacementNeedle = T())
    {
        try
        {
            if(replace)
            {

                return replaceOccurances(   sInput.begin(), sInput.end(),
                                            sNeedle.begin(), sNeedle.end(),
                                            sReplacementNeedle.begin(), sReplacementNeedle.end());
            }
            else
            {
                return countOccurances( sInput.begin(), sInput.end(),
                                        sNeedle.begin(), sNeedle.end());
            }
        }
        catch(MYException& e)
        {
            clog << "MyClass::_PerformStringOperation() - could not perform operation" << endl;
            clog << e.what();
            throw;
        }
        catch(exception& e)
        {
            clog << "MyClass::_PerformStringOperation() - Something more fundemental went wrong" << endl;
            clog << e.what();
            throw;
        }
    }
};

и сопровождающий CPP

std::string MyClass::_narrowNeedle("My");
std::wstring MyClass::_wideNeedle = std::wstring(L"My");
std::string MyClass::_narrowReplacementNeedle = std::string("MY");
std::wstring MyClass::_wideReplacementNeedle = std::wstring(L"MY");

size_t MyClass::getNiCount(std::string& sInput)
{ 
    try
    {
        return _PerformStringOperation(sInput,_narrowNeedle);
    }
    catch(...)
    {
        throw;
    }           
}

size_t MyClass::getNiCount(std::wstring& sInput)
{ 
    try
    {
        return _PerformStringOperation(sInput,_wideNeedle);
    }
    catch(...)
    {
        throw;
    }
}

size_t MyClass::replaceNiWith(std::string& sInput)
{
    try
    {
        return _PerformStringOperation(sInput,_narrowNeedle,true,_narrowReplacementNeedle);
    }
    catch(...)
    {
        throw;
    }
}

size_t MyClass::replaceNiWith(std::wstring& sInput)
{
    try
    {
        return _PerformStringOperation(sInput,_wideNeedle,true,_wideReplacementNeedle);
    }
    catch(...)
    {
        throw;
    }
}
0 голосов
/ 21 октября 2011

Проблема не указана, и вы переоцениваете это.

Какая статистика будет собираться? Какие изменения будут внесены в строки? Как долго будут струны? Имеет ли значение производительность?

Почему бы не пойти с простым решением: напишите все свои процедуры статистики / изменения строк для работы в UTF-8 (при условии, что это кодировка для ваших char s). Если вам нужно работать со строкой UTF-16, преобразуйте ее в UTF-8, вызовите работающую над ней подпрограмму, а затем преобразуйте измененную строку обратно в UTF-16. ПОЦЕЛУЙ.

Edit:

Существует еще одно соображение: является ли ваш алгоритм независимым от кодирования (т. Е. Не зависит ли он от кодирования строк), т. Е. Является ли единственная переменная «широтой» символов? Если это так, то в качестве альтернативы может быть использована шаблонная процедура, которая принимает начальный и конечный итератор, как предполагает Parapura.

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

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