Шаблоны проектирования: я должен использовать наследование здесь? - PullRequest
0 голосов
/ 16 апреля 2019

Введение / установка

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

Вот объявление класса подсветки синтаксиса (не беспокойтесь о языковых типах, которым предшествует «Q»; они просто определены каркасом Qt для C ++):

class Highlighter : public QSyntaxHighlighter
{
    Q_OBJECT

public:

    Highlighter(QTextDocument *parent = nullptr) : QSyntaxHighlighter (parent) {}
    virtual void addKeywords(QStringList keywords);
    virtual void setKeywordFormat();
    virtual void setClassPattern(QRegularExpression classPattern);
    virtual void setClassFormat();
    virtual void setFunctionPattern(QRegularExpression functionPattern);
    virtual void setFunctionFormat();
    virtual void setQuotePattern(QRegularExpression quotePattern);
    virtual void setQuoteFormat();
    virtual void setInlineCommentPattern(QRegularExpression inlineCommentPattern);
    virtual void setInlineCommentFormat();
    virtual void setBlockCommentStartPattern(QRegularExpression blockCommentStart);
    virtual void setBlockCommentEndPattern(QRegularExpression blockCommentEnd);
    virtual void setBlockCommentFormat();
    virtual void addRule(QRegularExpression pattern, QTextCharFormat format);

protected:

    virtual void highlightBlock(const QString &text) override;
    virtual void highlightMultilineComments(const QString &text);

private:

    struct HighlightingRule
    {
        QRegularExpression pattern;
        QTextCharFormat format;
    };

    QVector<HighlightingRule> rules;

    QRegularExpression blockCommentStart;
    QRegularExpression blockCommentEnd;

    QTextCharFormat keywordFormat;
    QTextCharFormat classFormat;
    QTextCharFormat inlineCommentFormat;
    QTextCharFormat blockCommentFormat;
    QTextCharFormat quoteFormat;
    QTextCharFormat functionFormat;
};

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

Кроме того, заголовок включает следующие функции, не являющиеся частью класса:

Highlighter *cHighlighter(QTextDocument *doc);
Highlighter *cppHighlighter(QTextDocument *doc);
Highlighter *javaHighlighter(QTextDocument *doc);
Highlighter *pythonHighlighter(QTextDocument *doc);

Каждая из этих функций собирает соответствующий тип маркера. Ниже приведены определения функций:

/* Returns a Highlighter object specific to the C language and its grammar and syntax.
 */
Highlighter *cHighlighter(QTextDocument *doc)
{
    QStringList keywords;

    keywords << "\\bauto\\b" << "\\bbreak\\b" << "\\bcase\\b" << "\\bchar\\b" << "\\bconst\\b"
             << "\\bcontinue\\b" << "\\bdefault\\b" << "\\bdo\\b" << "\\bdouble\\b" << "\\belse\\b"
             << "\\benum\\b" << "\\bextern\\b" << "\\bfloat\\b" << "\\bfor\\b" << "\\bgoto\\b"
             << "\\bif\\b" << "\\bint\\b" << "\\blong\\b" << "\\bregister\\b" << "\\breturn\\b"
             << "\\bshort\\b" << "\\bsigned\\b" << "\\bsizeof\\b" << "\\bstatic\\b" << "\\bstruct\\b"
             << "\\bswitch\\b" << "\\btypedef\\b" << "\\bunion\\b" << "\\bunsigned\\b" << "\\bvoid\\b"
             << "\\bvolatile\\b" << "\\bwhile\\b";

    QRegularExpression classPattern("\\b[A-Z_][a-zA-Z0-9_]*\\b");
    QRegularExpression quotePattern("(\".*\")|('\\\\.')|('.{0,1}')");
    QRegularExpression functionPattern("\\b[A-Za-z_][A-Za-z0-9_]*(?=\\()");
    QRegularExpression inlineCommentPattern("//.*");
    QRegularExpression blockCommentStart("/\\*");
    QRegularExpression blockCommentEnd("\\*/");

    Highlighter *highlighter = new Highlighter(doc);
    highlighter->addKeywords(keywords);
    highlighter->setClassPattern(classPattern);
    highlighter->setQuotePattern(quotePattern);
    highlighter->setFunctionPattern(functionPattern);
    highlighter->setInlineCommentPattern(inlineCommentPattern);
    highlighter->setBlockCommentStartPattern(blockCommentStart);
    highlighter->setBlockCommentEndPattern(blockCommentEnd);

    return highlighter;
}


/* Returns a Highlighter object specific to the C++ language and its grammar and syntax.
 */
Highlighter *cppHighlighter(QTextDocument *doc)
{
    Highlighter *cLanguage = cHighlighter(doc);
    QStringList cppOnlyKeywords;

    cppOnlyKeywords <<  "\\basm\\b" << "\\bbool\\b" << "\\bcatch\\b" <<
                        "\\bclass\\b" << "\\bconst_cast\\b" << "\\bdelete\\b" <<
                        "\\bdynamic_cast\\b" << "\\bexplicit\\b" << "\\bfalse\\b" <<
                        "\\bfriend\\b" << "\\binline\\b" << "\\bmutable\\b" <<
                        "\\bnamespace\\b" << "\\bnew\\b" << "\\boperator\\b" <<
                        "\\bprivate\\b" << "\\bprotected\\b" << "\\bpublic\\b" <<
                        "\\breinterpret_cast\\b" << "\\bstatic_cast\\b" <<
                        "\\btemplate\\b" << "\\bthis\\b" << "\\bthrow\\b" <<
                        "\\btrue\\b" << "\\btry\\b" << "\\btypeid\\b" << "\\btypename\\b" <<
                        "\\bvirtual\\b" << "\\busing\\b" << "\\bwchar_t\\b";

    cLanguage->addKeywords(cppOnlyKeywords);
    return cLanguage;
}


/* Returns a Highlighter object specific to the Java language and its grammar and syntax.
 */
Highlighter *javaHighlighter(QTextDocument *doc)
{
    QStringList keywords;

    keywords << "\\babstract\\b" << "\\bassert\\b" << "\\bboolean\\b" << "\\bbreak\\b" << "\\bbyte\\b"
             << "\\bcase\\b" << "\\bcatch\\b" << "\\bchar\\b" << "\\bclass\\b" << "\\bconst\\b" << "\\bcontinue\\b"
             << "\\bdefault\\b" << "\\bdo\\b" << "\\bdouble\\b" << "\\belse\\b" << "\\benum\\b" << "\\bextends\\b"
             << "\\bfinal\\b" << "\\bfinally\\b" << "\\bfloat\\b" << "\\bfor\\b" << "\\bgoto\\b" << "\\bif\\b"
             << "\\bimplements\\b" << "\\bimport\\b" << "\\binstanceof\\b" << "\\bint\\b" << "\\binterface\\b"
             << "\\blong\\b" << "\\bnative\\b" << "\\bnew\\b" << "\\bpackage\\b" << "\\bprivate\\b" << "\\bprotected\\b"
             << "\\bpublic\\b" << "\\breturn\\b" << "\\bshort\\b" << "\\bstatic\\b" << "\\bstrictfp\\b" << "\\bsuper\\b"
             << "\\bswitch\\b" << "\\bsynchronized\\b" << "\\bthis\\b" << "\\bthrow\\b" << "\\bthrows\\b" << "\\btransient\\b"
             << "\\btry\\b" << "\\bvoid\\b" << "\\bvolatile\\b" << "\\bwhile\\b" << "\\btrue\\b" << "\\bfalse\\b" << "\\bnull\\b";

    QRegularExpression classPattern("\\b[A-Z_][a-zA-Z0-9_]*\\b");
    QRegularExpression quotePattern("(\".*\")|('\\\\.')|('.{0,1}')");
    QRegularExpression functionPattern("\\b[A-Za-z_][A-Za-z0-9_]*(?=\\()");
    QRegularExpression inlineCommentPattern("//.*");
    QRegularExpression blockCommentStart("/\\*");
    QRegularExpression blockCommentEnd("\\*/");

    Highlighter *highlighter = new Highlighter(doc);
    highlighter->addKeywords(keywords);
    highlighter->setClassPattern(classPattern);
    highlighter->setQuotePattern(quotePattern);
    highlighter->setFunctionPattern(functionPattern);
    highlighter->setInlineCommentPattern(inlineCommentPattern);
    highlighter->setBlockCommentStartPattern(blockCommentStart);
    highlighter->setBlockCommentEndPattern(blockCommentEnd);

    return highlighter;
}


/* Returns a Highlighter object specific to the Python language and its grammar and syntax.
 */
Highlighter *pythonHighlighter(QTextDocument *doc)
{
    QStringList keywords;

    keywords << "\\band\\b" << "\\bas\\b" << "\\bassert\\b" << "\\bbreak\\b" << "\\bclass\\b" << "\\bcontinue\\b"
             << "\\bdef\\b" << "\\bdel\\b" << "\\belif\\b" << "\\belse\\b" << "\\bexcept\\b" << "\\bFalse\\b"
             << "\\bfinally\\b" << "\\bfor\\b" << "\\bfrom\\b" << "\\bglobal\\b" << "\\bif\\b" << "\\bimport\\b"
             << "\\bin\\b" << "\\bis\\b" << "\\blambda\\b" << "\\bNone\\b" << "\\bnonlocal\\b" << "\\bnot\\b"
             << "\\bor\\b" << "\\bpass\\b" << "\\braise\\b" << "\\breturn\\b" << "\\bTrue\\b" << "\\btry\\b"
             << "\\bwhile\\b" << "\\bwith\\b" << "\\byield\\b";

    QRegularExpression classPattern("\\b[A-Z_][a-zA-Z0-9_]*\\b");
    QRegularExpression quotePattern("(\".*\")|('.*')");
    QRegularExpression functionPattern("\\b[A-Za-z_][A-Za-z0-9_]*(?=\\()");
    QRegularExpression inlineCommentPattern("#.*");
    QRegularExpression blockCommentStart("'''");
    QRegularExpression blockCommentEnd("'''");

    Highlighter *highlighter = new Highlighter(doc);
    highlighter->addKeywords(keywords);
    highlighter->setClassPattern(classPattern);
    highlighter->setQuotePattern(quotePattern);
    highlighter->setFunctionPattern(functionPattern);
    highlighter->setInlineCommentPattern(inlineCommentPattern);
    highlighter->setBlockCommentStartPattern(blockCommentStart);
    highlighter->setBlockCommentEndPattern(blockCommentEnd);

    return highlighter;
}

Проблема

Обратите внимание на защищенный метод с именем highlightMultilineComments. По умолчанию из-за того, как в Qt выполняется подсветка синтаксиса, этот метод предполагает, что регулярные выражения Highlighter blockCommentStart и blockCommentEnd не идентичны. В случае таких языков, как Python, это явно не так, поскольку начальный и конечный разделители комментариев одинаковы (тройные одинарные или двойные кавычки). И в этом случае функция не работает должным образом. Это все, что вам нужно знать.

Проблемы с наследованием

Я сделал метод highlightMultilineComments виртуальным с намерением создать подкласс, скажем PythonHighlighter, который переопределяет только эту специальную функцию для определения пользовательской логики. Теоретически, другие языки могут переопределять все и настраивать то, как они хотят настроить подсветку (если я перейду с наследованием).

Но если бы я создал подкласс для Python, это означало бы, что я должен был бы создать его для C, C ++, Java и любого другого языка, который я хотел бы добавить в будущем (для согласованности). Очевидно, что это сложнее в управлении, чем мой нынешний подход, где у меня есть функции, которые просто собирают маркеры. Если я добавлю класс для каждого языка, количество исходных файлов значительно возрастет.

Проблемы с функциями компоновщика

Таким образом, использование функций компоновщика имеет свои преимущества. Но такой подход не позволяет мне переопределить метод highlightMultilineComments. Так что это явно не идеально в этом отношении.

Вопрос

Как я могу использовать преимущества наследования - возможность переопределять методы, подобные highlightMultilineComments, в зависимости от языка - без ущерба для относительной управляемости "функций построителя"?

Дополнительные вещи, которые я рассмотрел

Я также рассмотрел возможность добавления функции, подобной highlightSymmetricMultilineComments. Затем highlightMultilineComments может проверить, имеют ли blockCommentStart и blockCommentEnd одинаковый шаблон регулярных выражений. Если они имеют одинаковый шаблон, функция просто вызовет свой симметричный вариант.

Это представляет очевидную проблему - не имеет смысла, чтобы это было в Highlighter, учитывая, что не все языки имеют симметричные многострочные комментарии (Python - единственный, который в настоящее время поддерживается текстовым редактором, который поддерживает).

1 Ответ

1 голос
/ 16 апреля 2019

Рассмотрите возможность добавления commentHighlighter переменной-члена, представляющей функцию, которая выполняет фактическое выделение:

std::function<void(Highlighter&, const QString&)> commentHighlighter;

Затем мы можем написать highlightMultilineComments, чтобы она вызывала эту переменную-член:

void highlightMultilineComments(const QString &text) 
{
    // If the class has a valid commentHighlighter, use that
    if(commentHighlighter) 
    {
        commentHighlighter(*this, text); 
    } 
    else //Otherwise, use the default implementation
    {
        // Default implementation
    }
}

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

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