RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 608532
Accepted
Matty
Matty
Asked:2020-12-27 23:15:21 +0000 UTC2020-12-27 23:15:21 +0000 UTC 2020-12-27 23:15:21 +0000 UTC

用于处理笔记的程序的代码审查

  • 772

你好。我正在编写一个用于处理笔记的小程序。现在已经实现了最低限度的功能:可以创建、编辑和删除注释。对于 GUI,我有 Qt,基础是 SQLite。一般来说,一切正常,做我想做的事。但是我的编程经验趋于零,所以我怀疑有很多缺点我无法修复,因为我不知道去哪里找。我希望您指出所有明显的错误/问题/缺点。对从划分到类的逻辑到可能的内存泄漏的一切都感兴趣。

现在基本上有 12 个类:

  • 4 个窗口:MattyNotesMainWindow, MattySettingsDialog, addNoteDialog,MattyMessageBox
  • 使用数据库的类DbManager
  • 用于编写 SQL 查询的类QueryConstructor
  • 注解类本身MattyNote
  • 用于可视化显示注释的构造函数类MattyGroupBox
  • 排序和显示笔记的类NoteHolder
  • css控制类MattyStyleSheetEditor
  • 用于存储字符串和字符的类Constants
  • 手表MattyClocks

我知道不太可能有人会校对所有代码,因此也许您可以查看类及其方法并说出哪些是多余的,或者查看任何一个类并指出其中的缺陷。

例如连接数据库、编辑已有笔记、从数据库中删除所有笔记等功能:

bool DbManager::connect(const QString & path)
{
    MattyNotesDb = QSqlDatabase::addDatabase("QSQLITE");
    MattyNotesDb.setDatabaseName(path);

    if(QFile::exists(path))
    {
        if (!MattyNotesDb.open())
        {
            showIsNotOpenedError();
            MattyNotesDb.close();

            return false;
        }
        else
        {
            PathToDb = MattyNotesDb.databaseName();
        }

        return true;
    }
    else
    {
        return false;
    }
}

bool DbManager::editNote(MattyNote & Note, int NoteId)
{
    if (connected())
    {
        QueryConstructor Edit;
        Edit.setTableName(QStringLiteral("Notes"));

        Edit.addWhereFieldValue(QStringLiteral("NoteId"), QString::number(NoteId));

        QMap<QString, QString> NoteTemp;
        NoteTemp["NoteTitle"] = "\'" + Note.getTitle() + "\'";
        NoteTemp["NoteType"] = "\'" + Note.getType() + "\'";
        NoteTemp["NoteText"] = "\'" + Note.getText() + "\'";
        NoteTemp["EventTime"] = "\'" + Note.getEventTime() + "\'";
        NoteTemp["EventDate"] = "\'" + Note.getEventDate() + "\'";
        NoteTemp["CrTime"] = "\'" + Note.getCrTime() + "\'";
        NoteTemp["CrDate"] = "\'" + Note.getCrDate() + "\'";
        NoteTemp["TypeId"] = QString::number(Note.getTypeId());

        Edit.setWhatToSetFieldValue(NoteTemp); //

        QSqlQuery editNoteQuery;
        return editNoteQuery.exec(Edit.constructUpdateQuery());
    }
    else
    {
        return false;
    }
}

QVector<MattyNoteRow> DbManager::showNotes()
{
    if (connected())
    {
        QVector<MattyNoteRow> VectorOfNoteRows;

        QueryConstructor SelectAllNotes;
        SelectAllNotes.setTableName(QStringLiteral("Notes"));
        SelectAllNotes.setOrderByClause("NoteId", Descending);

        QSqlQuery getAllNotesQuery(MattyNotesDb);

        if( getAllNotesQuery.exec(SelectAllNotes.constructSelectQuery()))
        {
            while (getAllNotesQuery.next())
            {
                MattyNoteRow Row;

                Row.NoteId=getAllNotesQuery.value("NoteId").toInt();
                Row.NoteTitle=getAllNotesQuery.value("NoteTitle").toString();
                Row.NoteType=getAllNotesQuery.value("NoteType").toString();
                Row.NoteText=getAllNotesQuery.value("NoteText").toString();
                Row.EventTime=getAllNotesQuery.value("EventTime").toString();
                Row.EventDate=getAllNotesQuery.value("EventDate").toString();
                Row.CrTime=getAllNotesQuery.value("CrTime").toString();
                Row.CrDate=getAllNotesQuery.value("CrDate").toString();
                Row.TypeId=getAllNotesQuery.value("TypeId").toInt();

                VectorOfNoteRows.push_back(Row);
            }
        }
        else
        {
            QMessageBox::critical(NULL, QObject::tr("Error"), getAllNotesQuery.lastError().text());
        }

        return VectorOfNoteRows;
    }
    else
    {
        return QVector<MattyNoteRow>();
    }
}

将注释发送到表单:

    void NoteHolder::publishNotes(QWidget* ParentWidget)
    {
        erasePublishedNotes(ParentWidget);

        getAllNotes();

        QVector<class MattyNote>::iterator NoteNumber;
        int i;
        for (NoteNumber = ListOfAllNotes.begin(), i=0; NoteNumber < ListOfAllNotes.end();NoteNumber++, i++)
        {

            MattyGroupBox* MyGroupBox = new MattyGroupBox(*NoteNumber, ParentWidget);

            ParentWidget->layout()->addWidget(MyGroupBox);
        }
    }

void NoteHolder::erasePublishedNotes(QWidget* ParentWidget)
{
    MattyGroupBox* MgbTemp;
    while ((MgbTemp = ParentWidget->findChild<MattyGroupBox*>()) != 0)
    {
        delete MgbTemp;
    }

    QGroupBox* GbTemp;
    while ((GbTemp = ParentWidget->findChild<QGroupBox*>()) != 0)
    {
        delete GbTemp;
    }
}

void NoteHolder::getAllNotes()
{
    TotalNoteCount = 0;

    if (!ListOfAllNotes.isEmpty())
        ListOfAllNotes.clear();

    QVector<struct MattyNoteRow> ListOfRows = DbManager::showNotes();

    for (int i = 0; i < ListOfRows.length();i++)
    {
        MattyNote TempNote(ListOfRows[i]);
        ListOfAllNotes.append(TempNote);
        TotalNoteCount++;
    }
}

整个源代码可以在这里看到:GitHub

分离成类、它们的方法、调用、依赖关系等。这里:doxygen

c++
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. maestro
    2020-01-02T23:53:12Z2020-01-02T23:53:12Z

    让我试着回答一下,因为我们已经见过马蒂一次了……

    enum OrderType { Straight, Descending };
    

    升序排序称为Ascending。

    public:
        MattySettingsDialog(QWidget * parent = 0);
        ~MattySettingsDialog();
    

    继承类中的析构函数必须是虚函数,否则会破坏调用析构函数的顺序。MattyNotesMainWindow许多其他类也是如此。

    enum Type { MessageBoxInformation = 0, MessageBoxWarning = 1, MessageBoxQuestion = 2 };
    

    Type- 一个太容易发生冲突的名字。

    您正在使用 Doxygen 和一堆没有任何文本注释的图表,而这些图表很少有用(恕我直言)。Qt 项目本身有很好的文档记录,看看那里的一切是如何安排的。

    我也同意我的同事的意见,即您不应该自己生成查询。即使在我们寻找丢失的桌子时,我也注意到了这一点。这种解决方案不仅难以维护,而且还容易出现 SQL 注入。使用预先准备好的参数查询完全消除了注入。将来,当您使用网络数据库时,考虑这一点很重要。在实践中,很少有请求结构必须动态更改的情况,对于其他情况prepare来说,这已经足够了。不要忘记调用该方法时必须打开数据库prepare,否则行为未定义。我曾经偶然发现这个耙子。

    我建议我所有的类都继承自 QObject。否则,您将无法在其中使用信号和槽。例如,最有可能添加信号的候选者是 class DbManager,信号可能是“数据库状态更改”(连接建立/丢失)。

    将来,您需要考虑如何安装该程序。据我了解,它是跨平台的。在 Linux 上,Qt 可执行库是先决条件,因此通过将适当的信息添加到包配置文件,您将解决与计算机上库可用性相关的所有问题。如果是 Windows,您将需要手动收集所有的 dll 文件并将它们添加到安装包中。这些文件大致位于以下路径:“c:\Qt\5.6.1\5.6\Src\MinGW”。您需要确定您的程序需要哪些文件,并在安装期间将它们放在程序旁边。曾经有一次我使用 qmake 选择所有必要的文件,将它们放在一个单独的目录中并为 Inno Setup 生成一个脚本。如果你想做类似的事情,使用变量INSTALLS将所有文件放在一个公共目录中,以及编写 iss 脚本的 write_file 函数。

    • 2
  2. Best Answer
    Alex Krass
    2020-01-03T23:59:42Z2020-01-03T23:59:42Z

    决定包含文件的存储

    所有包含的文件都必须在头文件*.h中,但你的一半文件在*.cpp. 使应用程序看起来单一,删除标头中所有不必要的连接。

    通用路径

    现在你已经将大部分路径和设置硬连接到应用程序中,这非常糟糕,因为每次你都必须重新编译并且程序失去了它的通用性。用户以配置文件的形式使用更通用的工具要容易得多。

    配置.h

    class Config
    {
        public:
            static QString GetValue(QString);
        private:
            Config();
    };
    

    配置.cpp

    Config::Config() { }
    
    QString Config::GetValue(QString key)
    {
        QString configPath = QCoreApplication::applicationDirPath() + "/config.ini";
        QSettings settings(configPath,  QSettings::IniFormat);
    
        return settings.value(key).toString();
    }
    

    现在你可以从中清理掉一堆不需要的东西DbManager,也可以阅读文档,可以从中学到很多东西。现在您不需要传递行,它们将从文件本身读取,config.ini或者路径将默认设置为包含可执行文件的目录。此外,您不会关闭数据库连接,所以我认为您可以使用一个DbManager::MattyNotesDb.isOpen()简单的DbManager::connected().

    QString DbManager::StoredPathDB = "";
    QSqlDatabase DbManager::MattyNotesDb;
    
    QString DbManager::GetPathDB()
    {
        if(DbManager::StoredPathDB.isNull() || DbManager::StoredPathDB.isEmpty())
        {
            QString cPath = Config::GetValue("PathToDb");
            if(cPath.isNull() || cPath.isEmpty())
            {
                cPath = QCoreApplication::applicationDirPath() + "/MattyNotes.sqlite";
            }
    
            DbManager::StoredPathDB = cPath;
        }
    
        return StoredPathDB;
    }
    
    bool DbManager::connected()
    {
        return DbManager::MattyNotesDb.isOpen();
    }
    
    bool DbManager::connect()
    {
        MattyNotesDb = QSqlDatabase::addDatabase("QSQLITE");
        MattyNotesDb.setDatabaseName(DbManager::GetPathDB());
    
        if (!MattyNotesDb.open())
        {
            showIsNotOpenedError();
            MattyNotesDb.close();
    
            return false;
        }
    
        return true;
    }
    

    现在您可以将应用程序放在 USB 闪存驱动器上,它应该可以在任何地方运行。

    单一职责原则

    每个类都应该为自己的目的负责,它不应该承载多个功能。不要忘记在它们内部移动实体的形成,而不会将它们涂抹在其他类的代码上。

    MattyNoteRow(QSqlQuery query)
    {
            NoteId=query.value("NoteId").toInt();
            NoteTitle=query.value("NoteTitle").toString();
            NoteType=query.value("NoteType").toString();
            NoteText=query.value("NoteText").toString();
            EventTime=query.value("EventTime").toString();
            EventDate=query.value("EventDate").toString();
            CrTime=query.value("CrTime").toString();
            CrDate=query.value("CrDate").toString();
            TypeId=query.value("TypeId").toInt();
    }
    

    现在查询所有笔记看起来会更好:

    while (getNotesQuery.next())
    {
        VectorOfNotes.push_back(MattyNoteRow(getNotesQuery));
    }
    

    关于数据库的更多信息

    一般来说,DbManager::MattyNotesDb对所有事情都使用一个连接是很好的,静态方法不是最好的出路。当应用程序突然获得多线程或一些其他实体时,问题就会开始。因此,这种做法最好不要使用。不要害怕在模型中编写不灵活的方法。设计模型与设计应用程序有很大不同。动态整形是最后的手段。

    这是因为以这种形式重新创建查询并在数据库上执行它或捕获错误是相当困难的。此外,几个人经常在一个项目上工作,明确的请求使沟通更容易。有时查看请求并找到错误或需要更改的地方就足够了。动态编队剥夺了你这一点,你将不得不铲除所有代码并通过代码重新创建请求。因此,更经常提出苛刻的要求,但效率更高。

    为实体创建一个单独的类也是值得的。最后它应该是这样的。

    数据库管理器.cpp

    QSqlDatabase DbManager::GetInstance()
    {
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName(DbManager::GetPathDB());
    
        return db;
    }
    

    MattyNoteProvider.cpp

    MattyNoteRow MattyNoteProvider::ShowNote(int id)
    {
        QSqlDatabase db = DbManager::GetInstance();
    
        if (db.open())
        {
            QString sql = "SELECT * FROM Notes WHERE NoteID = :noteID ORDER BY ASC";
    
            QSqlQuery query;
            query.prepare(sql);
            query.bindValue(":noteID", id);
            query.exec();
    
            ...
    
            db.close();
            return row;
        }
    }
    
    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5