http://blog.sina.com.cn/s/articlelist_1935155723_0_1.html
http://blog.sina.com.cn/s/articlelist_1581720921_0_1.html
http://www.geek-workshop.com/portal.php?mod=list&catid=1
arduino 连接蓝牙线路图:
http://blog.sina.com.cn/s/blog_5e4725590100d9mt.html
货物运输系统概述
我们要为一个货运公司开发新软件。最初有3个基本功能
1. 跟踪顾客货物的关键装卸事件
2. 对货物进行事先预约
3. 当货物在装卸中抵达某个点时,自动向顾客发送发票
这个模型组织了领域知识,并为开发团队提供了一种语言。我们可以给出如下陈述:
1. 多个cunsotmer涉及到一个Cargo,每个Customer充当一种不同的role。
2. 已经指定了提供目标
3. 满足Specification的一系列Carrier Movement将实现提货目标
模型中的每个对象都有清晰的含义:
1. Handling Event装卸事件是发生在Cargo上的一种离散活动,例如货物装船或者出关。这个类也许可以细分为一个包含不同种类事件的层次结构,例如装货,卸货,或者收货人提货等。
2. Delivery Specification提货规格定义了一个提货目标,它至少包含一个目的地和一个抵达时间,但是还可以更加复杂。这个类遵循规格模式(参加第9章)
3. Delivery Specification的职责本来可以由Cargo对象来承担,但是把它抽象出来至少可以有3个好处:
a.如果没有Delivery Specification,所有与指定提货目标有关的属性和关联就都要由 Cargo对象来负责。这会使Cargo变得散乱,理解或修改会更困难。
b.当把模型作为一个整体来说明时,这种抽象使我们可以很容易且安全地把细节隐藏起来。例如,Delivery Specification可能还封装了其他的标准,但是在这个细节层次上,我们并不需要把它暴露出来。这个图是告诉读者,这里有一个提货规格,但是它的细节并不重要,无需考虑。
c.这个模型更富有表达能力。添加Delivery Specification把问题说得很明白了:运输Cargo的实际方式可以自由确定,但是它必须达到由Delivery Specification给出目标。
4. role将顾客在一次运输中扮演的不同角色区分开来。例如,有“托运人”、“收货人”等。对于一件特定的Cargo,一个顾客只能扮演一个角色,因此Customer和Cargo之间的关联成为限定的多对一关联,而不是多对多关联。角色可以实现为一个简单的字符串,如果还需要其他行为的话,也可以实现为一个类。
5. Carrier Movement代表一个特定的Carrier(例如一辆卡车或者一艘货轮)从一个Location到另一个地点的转移。Cargo就可以装上Carrier, 在一个或者多个Carrier Movement期间,从一个地方转移到另一个地方。
6. Delivery Specification描述目标,而Delivery History(提货历史)则反映了发生在Cargo上的实际行为。通过分析最后一次装卸或卸货对应的Carrier Movement的目的地,我们可以从Delivery History对象计算出Cargo的当前位置。如果提货成功的话,那么Delivery History最后应该满足Delivery Specification的目标。、
区分实体和值对象
1. Cunstomer
我们从一个容易一些的对象开始。一个Customer对象代表一个人活着一个公司,在通常意义下这是一个实体。
2. Cargo
两个同样的货箱必须区分,因此Cargo货物对象是实体。
3. Handling Event(装卸事件)和Carrier Movement(承运人运输)
我们关心每个个体事件,因为我们需要这些信息来跟踪进展。这些事件反映了现实世界中发生的事件,通常是不可互换的,因此它们是实体。每个Carrier Movement都会从运输计划中得到一个代码作为标识。
4. Location(地点)
名称相同的两个地点不是同一个地点。
5. Delivery History
Delivery History有些复杂。Delivery History是不可互换的,因此它们是实体。但是Delivery History与它发运的Cargo具有一对一关联,因此它实际上并没有自己的标识。它的标识是从所属的Cargo借用过来的。用聚合来建模将使问题更加清晰。
6. Delivery Specification(提货规格)
虽然Delivery Specification代表Cargo的目标,但是这个抽象并不依赖于Cargo。它实际上描述了某些Delivery History的假象状态。我们希望与一件Cargo连接的Delivery History最终能够满足与它连接的Delivery Specification;但是,虽然二者的历史在最开始时都相同,但它们不能共享同一个Delivery History。Delivery Specification是值对象。
7. Role(角色)和其他属性
Role角色提供了与其限定的关联有关的信息,但是它没有历史的或连续的要求。它是一个值对象,可以在不同的Cargo/Customer关联中进行共享。
其他属性,如时间戳或者名字,都是值对象。
聚合的边界
选择仓储
设计中有5个实体是聚合根。我们只需考虑这几个对象,因为其他对象都不允许有仓储。
为了决定哪些候选者确实需要仓储,我们必须重温一下应用需求。为了在Booking Application中进行预约,用户需要选择不同角色(托运人、收货人等)的Customer。因此,我们需要一个Customer Repository。用户还要查找Location来指定Cargo的目的地,因此Location Repository也是需要的。
Activity Logging Application需要允许用户查找装载了给定Cargo的Carrier Movement,因此我们需要一个Carrier Movement Repository。用户需要告诉系统哪个Cargo已经装载,因此还需要一个Cargo Repository。
对象的创建
我们将每个要创建的对象放到工厂里,然后传入需要的参数,然后开始创建。
遗憾的是,故事并不像这么简单。Cargo到Delivery History到Handling Event再回到Cargo的这个引用循环把事例创建问题搞复杂了。Delivery History持有一个与其Cargo相关的Handling Event集合,而新创建的Handling Event对象必须在同一个事物中加入到这个集合中来。如果不创建这种反向指针,对象将使不一致的,如下图:
我们会发现一个动作会牵扯到两个操作,那么这个动作就是事物的,这样有出现一定的问题,下面我们给出另一种解决办法。
实际上,当把数据库作为底层技术时,我们可以设法用内部执行的查询来模拟对象集合。使用查询而不是集合使我们能更容易地维护Cargo和Handling Event之间的循环引用的一致性。
我们为Handling Event增加一个仓储来承担查询的职责。Handling Event Repository将提供一个根据给定Cargo查询其相关Event的功能。此外,仓储还可以为某些特定的问题进行优化,使其获得更高的查询效率。例如,为了推断Cargo的当前状态,我们需要根据Delivery History来查找最后一次报告的装货和卸货。如果这个访问路径使用非常频繁,那么我们可以设计一个专门的查询,仅仅返回与最后一次报告相关的Handling Event。如果我们希望用一个查询来得出某个给定Carrier Movement中装载的所有Cargo,也只要增加一个查询就行了。
Greetings fellow developers!
This is part 3 of a series on implementing the basic interfaces to provide features to Asterisk.
Part 1 – Basic Module Structure
Part 2 – CDR Handling Interface
In this section, you will see how to implement an Asterisk CLI command. CLI commands are extremely useful for showing configuration, statistics, and other debugging purposes.
We are going to continue editing the module from part 2, res_helloworld2.c.
The first step is to include the header file which defines the CLI command interface.
#include "asterisk/cli.h"
Here is the code for a CLI command, “echo”. It simply prints back the first argument to the CLI command. The individual parts of this function will be described later.
static char *handle_cli_echo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { switch (cmd) { case CLI_INIT: e->command = "echo"; e->usage = "Usage: echo <stuff>\n" " Print back the first argument.\n" "Examples:\n" " echo foo\n" " echo \"multiple words\"\n" ""; return NULL; case CLI_GENERATE: return NULL; } if (a->argc == e->args) { ast_cli(a->fd, "You did not provide an argument to echo\n\n"); return CLI_SHOWUSAGE; } ast_cli(a->fd, "%s\n", a->argv[1]); return CLI_SUCCESS; }
The first line of this CLI handler matches the defined function prototype for CLI command handlers. The ast_cli_entry argument includes static information about the CLI command being executed. The cmd argument is set when the CLI command is being invoked for a reason other than normal execution from the CLI, which will be explained more later. The ast_cli_args argument contains information specific to this one time execution of the command, like the arguments to the command.
static char *handle_cli_echo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
The switch statement at the beginning of the function handles when the cmd argument is set to indicate that the function is being called for a reason other than normal execution. There are two cases handled.
- CLI_INIT. Every CLI command handler is called one time with a cmd of CLI_INIT. This asks the function to fill in the documentation on what the command is, as well as usage documentation.
- CLI_GENERATE. This cmd is used for implementing tab completion. CLI commands that provide tab completion of arguments must add code here. Implementing tab completion may be explained in a later tutorial. For now, there are many examples throughout the code.
The next code snippet ensures that at least one argument has been provided to the function. It is worth noting here that the ast_cli_entry argument is used to retrieve how many arguments define the command itself and the ast_cli_args argument is used to retrieve how many arguments were actually specified at the CLI when executing this command. If they are equal, then simply “echo” was provided.
if (a->argc == e->args) {
ast_cli(a->fd, "You did not provide an argument to echo\n\n");
return CLI_SHOWUSAGE;
}
Finally, we print a single argument to the CLI, and return that the execution of the CLI command was successful.
ast_cli(a->fd, "%s\n", a->argv[1]);
return CLI_SUCCESS;
The next thing that we have to add is the table of CLI commands included in this module. We will use AST_CLI_DEFINE() to add a single entry to this table. AST_CLI_DEFINE includes the pointer to the CLI command handling function, as well as a brief summary of what the command does.
static struct ast_cli_entry cli_helloworld[] = {
AST_CLI_DEFINE(handle_cli_echo, "Echo to the CLI"),
};
Finally, as discussed in part 2, we have to modify load_module and unload_module to register and unregister the CLI command table with the Asterisk core.
In unload_module, we add this:
ast_cli_unregister_multiple(cli_helloworld, ARRAY_LEN(cli_helloworld));
In load_module, we add this:
ast_cli_register_multiple(cli_helloworld, ARRAY_LEN(cli_helloworld));
That’s it! Recompile and reinstall the module. Take a look at res_helloworld3.c, for the completed code.
You can then try it out.
*CLI> help echo Usage: echo Print back the first argument. Examples: echo foo echo "multiple words" *CLI> echo You did not provide an argument to echo Usage: echo Print back the first argument. Examples: echo foo echo "multiple words" *CLI> echo foo foo *CLI> echo hello world hello *CLI> echo "hello world" hello world