【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
软件开发一般有软件需求、架构设计和详细设计、软件测试这四个部分。软件需求和软件测试都比较好理解,前者是说要实现哪些功能,后者是说做好的功能怎么测试。而架构设计和详细设计很多同学不太清楚,这两者的区别是什么。所谓的架构设计,其实就是业务的主流程是什么,一般来说,软件开发好之后,会做成一个通用产品,然后根据客户的需求做定制化开发。这是一般的做法,不然针对每个客户都要做一次定制开发,成本是受不了的。
而软件架构设计,就是摒弃不同客户之间的差异,提取公共的业务开发流程,这样的设计就是架构设计。它不限于用什么os、什么db,或者说用什么框架,它聚焦于业务的抽象和提炼。比如插件式开发,mvc开发,client-server开发,分布式开发,单机版运行再或者是抽象出来的业务逻辑,这些都属于架构设计的部分。架构设计聚焦于数据在模块之间的处理逻辑,而不是模块内的实现细节。
而详细设计,则是将软件架构设计中的一个一个模块进行细化处理,比如什么的数据结构、怎么并发、怎么实现缓存、怎么做好增删改查等等,这些部分都是详细设计的范畴。一开始写软件的时候,很容易把详细设计和架构设计等同起来,等到项目做的多了,就会从一类项目中提取出公共的框架,自然而然就有了架构设计的理念和想法。
1、架构设计的文档
很多同学喜欢直接写代码,不考虑架构设计文档,这是不好的做法。一件事情如果没有考虑清楚,或者考虑的不是很完善,很容易推导重来,这样开发的效率反而是变得很慢。所以说,我们在开发软件的时候一定要做好文档的编写。
2、写一部分代码,然后开始做架构设计文档
这部分代码可以很少,比如只是一个抽象流程,大约200-300行的范围。里面的数据结构完全可以不要,只要保证整体编过就可以了。代码的好处就是比较直观和方便,让我们知道当前的架构还有什么不足,能不能在一定程度上满足未来需求的拓展和补充。
3、代码和架构文档交叉完成
编写代码的时候,可以同时写架构文档。觉得文档不对的地方,可以同步更改下代码。或者说代码不好实现的部分,就可以同时修改下文档,这都是可以的。个人非常不建议天马行空地去做文档,只有文档和代码bind在一起,才能去验证自己想法的合理性。一般来说,与架构匹配的代码可以很短,几十、几百行的代码就可以说明问题。
4、一个固件主流程实现的范例
我们用树莓派4b实现一个图像处理的主流程。第一步,可以考虑下,整个软件怎么跑起来,比如说加载ini文件、启动xmlrpc server、准备method绑定等等;第二步,考虑客户可能会有哪些功能,这些功能怎么放到整体的代码逻辑当中去;第三步,针对前面讲到的插件和调度引擎,这部分究竟该怎么加载、怎么实现。紧紧围绕这三点,我们就可以写出一个简单的框架伪代码。这个伪代码是完全可以运行的,只不过没有具体的数据而已。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <iostream>
using namespace std;
// class definition
class Plugin
{
public:
Plugin() {}
~Plugin() {}
bool execute(string param) { return true;}
};
// command from client
class ReceiveFile
{
public:
ReceiveFile() {}
~ReceiveFile() {}
void exec() {}
};
class UpdateParam
{
public:
UpdateParam() {}
~UpdateParam() {}
void exec() {}
};
static void startNewWorkThread();
class RunWorkThread
{
public:
RunWorkThread() {}
~RunWorkThread() {}
void exec() { startNewWorkThread(); }
};
// function declaration
void initLog() {}
void loadIniFile() {}
void launchXmlRpcServer() {}
void runXmlRpcServer() {}
void shutdownSystem() {}
void checkWorkParam() {}
void loadRelavantPlugins() {}
void releaseRelevantPlugins() {}
Plugin pickPlugin(int id) { return Plugin(); }
int getNextPluginId(int id) { return 0; }
static bool runSingleProcedure()
{
Plugin plugin;
string pluginParam;
bool result;
int id = 0;
while (1)
{
plugin = pickPlugin(id);
result = plugin.execute(pluginParam);
if (!result)
{
break;
}
id = getNextPluginId(id);
}
return result;
}
static void startWorkEngine(string workParam)
{
bool runOnce = true;
checkWorkParam();
loadRelavantPlugins();
// check if run once or loop run
if (runOnce)
{
runSingleProcedure();
}
else
{
while (true == runSingleProcedure())
{
sleep(0.05);
}
}
releaseRelevantPlugins();
}
static void startNewWorkThread()
{
string workParam;
startWorkEngine(workParam);
}
// main file starts here
int main(int argc, char* argv[])
{
initLog();
loadIniFile();
launchXmlRpcServer();
runXmlRpcServer();
shutdownSystem();
return 0;
}
5、调试和验证
有些时候,嵌入式调试并不方便,可能windows上面的vs调试更加方便一点。以上面的代码为例,其实我们只需要根据平台判断当前是哪个,选择不同的头文件和函数即可,这样一份代码就可以在两个平台上都可以运行了。
比如说,假设是windows平台,添加的内容如下,
#include <Windows.h>
Sleep(50);
反之,如果是linux平台,则添加的内容如下,
#include <unistd.h>
sleep(0.05);