实现批量命令的并行处理
实现批量命令的并行处理传统上, Restful的命令都是同步执行的, 一条命令执行完毕后再执行另一条.但是, 我们的产品的一条命令需要批量执行多条命令的查询, 下载几十甚至上百个文件. 如果串行执行, 会消耗大量的时间在等待上, 包括服务器的命令执行等待和网络传输的等待.
一种做法是简单地启动线程池, 每个命令一个线程, 最后等待完成. 但是并不好, 本身, Qt提供的QNetworkAccessManager支持异步处理. 我们需要实现批量发送命令, 并且同步等待所有结果完成. 即我们将整个的多个命令的发送和接受作为一个阻塞式的任务, 批量发送, 异步处理, 并且等待所有命令结果都收到后再返回.
下面是批量下载文件的接口的简化实现:
paths是要下载的图片的网络地址的数组.
id_list是每个要下载的图片的标识ID, 用于区分识别.最终返回的是一个QMap<id, QPixmap>.
1234567891011121314151617181920QMap<QString, QPixmap> DeepApi::batchDownloadImag ...
Marshal的设计
Marshal设计概述Marshal界面如下图所示:
使用Graphiscs Scene-View架构. 主要工作都在Scene类MarshalScene中完成.
主要类列表:
MarshalScene QGraphicsScene类
MarshalView QGraphicsView类
ChromosomePixmapItem 用于显示染色体Item的QGraphicsItem类
GroupLine: 表示一类染色体的Item.
ItemGroup: 表示一行的布局
PatternItem: 表示染色体模式图
Notation, NotationHead, NotationTail, NotationEdge : 批注对象
GroupTemplate : 定义Scene中布局的模板
MarshaSceneMarshalScene是用户对染色体做分类操作的主要界面后端Scene. 因为我们的View类几乎什么也没有做, 所以主要的用户交互都在Scene中实现.
同时, 它也是一个数据容器.
123456789QMap<int, GroupLine*> _li ...
Extract的设计
ExtractScene设计染色体Item设计染色体显示对象在界面中, 在分析视图和提取视图中需要显示. 它们都是QGraphicsItem的派生类.
ExtractScene中的染色体显示很简单, 在基本场景下, 只需要在分裂相的图片上面标出染色体的轮廓即可. 只有少数场景下需要显示染色体的图片(对染色体图片做了忒别的增强处理).
我们通过染色体识别, 本身就得到了染色体在图像中的轮廓, 这个信息直接被用于构建Item. 最接近的就是QGraphicsPolygonItem. 因此,
定义类ChromosomeContourItem, 作为QGraphicsPolygonItem的派生类. 剩下的就是其他的一些控制信息, 比如是否显示轮廓, 轮廓的显示控制, 是否显示类别标签, 是否显示图片, 右键菜单等. 基本上没有特别需要关注的内容.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 ...
QtConcurrent中的run()函数的使用
QtConcurrent中的run()函数的使用有两种模式: Basic模式和Promise模式.
好像Promise模式是Qt6里面新增的, 以前没有
Basic模式的使用:使用1: 使用一个外部全局函数123456789// 最简单的场景: func1()是一个无参数的函数extern void func1();QFuture<void> future = QtConcurrent::run(func1);// 稍微复杂一点, 带参数场景:extern void func2(int arg1, double arg2);int arg1 = ...;double arg2 = ...;QFuture<void> future = QtConcurrent::run(func2, arg1, arg2);
场景2: 重载的全局函数12345678910111213// foo有两个重载, 要使用foo/1:void foo(int arg1);void foo(int arg1, double arg2);// 方法1: 使用Lambda, QFutu ...
合集
Part I 创建模式
从函数中返回对象
Chapter 2 Builder
2.1 Scenario
2.2 Simiple Builder
2.3 Fluent Builder
2.4 Communicating Intent
2.5 Groovy-Style Builder
2.6 Composite Builder
2.7 Summary
Chapter 3 Factory
3.1 Scenario
3.2 Factory Method 工厂方法
3.3 Factory
3.4 Inner Factory
3.5 抽象工厂Abstract Factory
3.6 Functional Factory
3.7 总结
Chapter 4 Prototype
4.1 构造对象
4.2 Ordinary Duplicate
4.3 Duplication via Copy Construction
4.4 Serialization的问题
4.5 Prototype Factory 原型工厂
4.6 总结
Chapter 5 Singleton
5.1 Sin ...
命令模式
考虑一个简单的赋值语句, 例如, meaning_of_life = 42. 这个变量被赋值了, 但是却没有办法来记录何时, 何地发生的赋值, 我们也无法得知它以前的值是什么. 这样可能是有问题的, 如果没有变更记录, 我们就无法回滚到以前的值, 无法完成审计, 或基于历史的调试.
而命令模式的建议是不要直接使用对象的API来操作它们, 而是发过去一个命令对象, 告诉它该如何做. 一个Command就是一个数据类, 它的成员函数描述了要做什么和如何做.
14.1 场景我们考虑对银行账户进行建模. 账户又一个余额属性(balance)和一个透支限额(overdraft limit)属性. 我们实现两个方法: deposit() 和withdraw().
12345678910111213141516171819struct BankAccount{ int balance = 0; int overfraft_limit = -500; void deposit(int amount) { balance += amount; ...
Maybe,Monad
C++有多种方式来表示某个”对象”是否有值:
使用nullptr来表示没有值
使用智能指针(例如, shared_ptr). 它提供了检测手段
std::optional<T>是一种库解决方案. 它可以存储T值或std::nullopt.
如果我们打算使用nullptr的方法, 假定我们的领域对象定义了一个Person对象, 它可能有一个Address成员属性, 而后者有一个可选的house_name属性.
至少在英国, 有些房子是有名字的. 例如, 如果你买一座城堡, 它的地址可能不是”123 Londo Road”, 而是”Montefiore Castle”, 而这也是它的地址.当然, 不是所有的house都有name.
12345678910struct Address{ string* house_name = nullptr;}struct Person{ Address* address = nullptr;}
我们关心的是, 比如, 如何安全地打印出某个人的house name, 如果它存 ...
职责链
场景考虑一个电脑游戏, 里面的每种生物都有一个名字和两个属性: 攻击力(attack)和防御力(defense):
1234567struct Creature{ string name; int attack, defense; // 构造函数和<<操作符 ...}
在游戏中, 角色可能会拿到一些装备(比如, 一把魔法剑), 或者终结能力的增强. 不管哪种情况, 他的攻击力/防御力都会发生变化. 我们用CreatureModifier来修改它的属性.
更进一步说, 可能会有多个Modifier作用到角色上, 这种情况在游戏中并不罕见. 因此, 我们需要按照他们获取装备的次序来将这些Modifier依次作用在角色上.
首先来看一个实现:
Pointer Chain1234567891011121314151617181920212223class CreatureModifier{ CreatureModifier* next{nullptr};protected: e ...
代理模式
我们在Decorator设计模式的时候看到可以使用不同的方法来增强一个对象的功能. Proxy模式也一样, 但是它的目的通常是在尽可能地保留原有API的功能的基础上提供内部的功能增强.
Proxy并不是一种同质(homogeneous)API. 因为人们建立了各种不同的proxy来实现不同的目的. 在本章中我们会选择性地介绍一些, 你可以在网络上看到更多的proxy.
12.1 Smart Pointers最简单直观的Proxy模式的使用是智能指针. 智能指针是对指针的封装, 它会持有引用计数, 重载某些操作, 但是, 总体来说, 会提供和普通指针一样的操作接口.
12345678910struct BandAccount{ void deposit(int amount) {...}};BandAccount *ba = new BankAccount;ba->deposit(124);auto ba2 = make_shared<BandAccount>();ba2->deposit(123); // sam ...
Flyweight
Flyweight, 有时也称为token或cookie, 是一种临时性的组件, 它起着智能引用smart reference的作用. 它通常是在当存在大量十分相似的对象时, 节省内存的一种手段.
11.1 User Names假定你有一个大型多人在线游戏, 相信会有很多人叫John Smith. 因此, 如果我们用ASCII码一个个记录他们的名字, 每个用户会消耗11个字节. 相反, 我们可以存储John Smith一次, 然后为每个人存储一个指向这个名字的指针. 这样就只需要8个字节.
更进一步, 我们还可以将John Smith再一次拆分为两部分分别保存.
1234567891011121314typedef uint32_t key;struct User{ User(const string& first_name, const string& last_name) : first_name{ add(first_name)}, last_name{ add(last_name)} ...