领域驱动设计-通用模型
通用模型
要将模型作为语言的骨干。团队在所有的交流与代码中都应该练习使用这种语言。在图、文档编写,尤其是发表意见过程中都使用相同的语言。
通过用替代表达方法进行实验来消除实际中遇到的困难,它们反映了替代模型的含义。然后按照新的模型重新进行编码,并对类、方法和模块进行重命名。在交谈中辨明术语中含糊的词义,就像我们对普通的词语意义达成一致意见那样。
要意识到通用语言中的变化也是模型中的变化。
领域专家应该排除掉传达领域意思时不易使用或不适当的术语或结构;开发人员应该注意防止可能导致设计失败的模糊性或不一致性。
利用对话改进模型
结合模型来讨论系统。使用模型的元素和元素之间的交互来大声描述场景,按照模型允许的方式把概念组合在一起。找到更简单的方式来说出要表达的内容,然后将这些意见应用到图形和代码中。
领域驱动设计-模型和实现
当开发人员开始实现应用程序的时候,他们发现彼此纠缠的关系(模型)根本无法转换成事务完整性操作中的可存储、可检索的单元(代码)。基于概念的对象不能够成为他们设计的基础。
模型驱动设计
领域驱动设计提倡一个模型不仅仅能够帮助早期的分析人员,并且可以作为设计的基础。这种方法要求对代码有着重要的暗示作用。不太明显的一点就是领域驱动设计需要一种不同的建模方法。
如果一个设计,或者它的核心部分,不能够映射到领域模型上,那么这个模型是没有价值的,软件的正确性也是值得怀疑的。同时,模型与设计功能之间复杂的映射常常难于理解,并且在实践中,当设计发生变化时也不可能维护。在分析与设计之间存在致命的隔阂,从任何(分析和设计)活动中获得的知识都无法提供给另一方。
应该设计出能够非常精确地反映领域模型的部分软件系统,这样映射就会明显。如同您尽力让模型更深层次地反映领域一样,再次研究模型并修改,使得它能够让软件更加自然的实现。需要这样的单个模型,除了要支持一种健壮的通用语言之外,还要很好的完成这两个目的。
从模型中得到的术语可以用于设计和职责的基本分配。代码成为模型的表达形式,因此对代码的改变可能也变成对模型的一种改变。它的影响将相应波及到项目中的其他活动。
要将实现与模型严格的绑定在一起,通常需要支持建模泛型的软件开发工具和语言,例如面向对象的编程。
Java-集合
Set
集合类型 | 描述 |
---|---|
HashSet | 一种没有重复元素的无序集合 |
TreeSet | 一种有序集 |
EnumSet | 一种包含枚举类型值得集 |
LinkedHashSet | 一种可以记住元素插入次序的集 |
Map
集合类型 | 描述 |
---|---|
HashMap | 一种存储键/值关联的数据结构 |
TreeMap | 一种键值有序排列的映射表 |
EnumMap | 一种键值属于枚举类型的映射表 |
LinkedHashMap | 一种可以记住键/值项添加次序的映射表 |
WeakHashMap | 一种其值无用武之地后可以被垃圾回收器回收的映射表 |
IdentityHashMap | 一种用==,而不是用equals比较键值得映射表 |
List
集合类型 | 描述 |
---|---|
ArrayList | 一种可以动态增长和缩减的索引序列 |
LinkedList | 一种可以在任何位置进行高效的插入和删除操作的有序序列 |
Queue
集合类型 | 描述 |
---|---|
PriorityQueue | 一种允许高效删除最小元素的集合 |
ConcurrentLinkedQueue | |
ArrayBlockingQueue | |
LinkedBlockingQueue | |
PriorityBlockingQueue | |
DelayQueue | |
SynchronousQueue |
Java并发编程实战
- 安全性问题。永远不发生糟糕的事情。
- 活跃性问题。某件正确的事情最终会发生。比如死锁,饥饿,活锁。
- 性能问题。在活跃性问题的基础上关注正确的事情尽快发生。例如服务时间过长,响应不灵敏,资源消耗过高等。
安全性问题
常用的注解:@ThreadSafe,@NotThreadSafe,@Immutable
- 原子性。避免竞态条件,以原子的方式执行,AtomicLong。
- 加锁机制
- 可见性
- 发布与逸出
- 线程封闭
- 栈封闭
- 不变性
活跃性问题
性能问题
AQS
AbstraceQueuedSynchronizer是一个用于构建锁和同步器的框架,许多同步器都可以通过AQS很容易并且高效地构造出来。
它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。
Maven指南
项目对象模型(POM)
插件和目标
一个Maven插件是一个单个或者多个目标的集合。如下面的例子,archtype是一个插件标识,create是目标标识1
mvn archtype:create
生命周期
命令行并没有指定一个插件目标,而是指定了一个Maven生命周期阶段。命令行是如何执行目标的呢?
插件目标可以被绑定到生命周期阶段上。每个阶段可以绑定0个或者多个目标。当执行到该阶段就会执行该阶段上的目标。
Maven中有三种标准的生命周期:清理(clean),默认(default)(有时候也称为构建),和站点(site)。默认的Maven生命周期包含了process-resources, compile, process-classes, process-test-resources, test-compile, test, prepare-package, package等等。
1 | mvn package |
坐标
POM定义了项目,提供了项目的一组唯一标识符(坐标),并且通过依赖(dependencies),父(parents)和先决条件(prerequisite)来定义了和其他项目的关系。
一个Maven坐标可以精确的定位一个项目。格式如下:1
groupId:artifactId:packageing:version
仓库
Maven仓库的标准是按照下面的目录格式来存储构件:
1 | /<groupId>/<artifactId>/<version>/<artifactId>-<version>.<packaging> |
依赖管理
一个复杂的项目将会包含很多依赖,也有可能包含依赖于其它构件的依赖。这是Maven最强大的特征之一,它支持了传递性依赖(transitive dependencies)。假如你的项目依赖于一个库,而这个库又依赖于五个或者十个其它的库(就像Spring或者Hibernate那样)。你不必找出所有这些依赖然后把它们写在你的pom.xml里,你只需要加上你直接依赖的那些库,Maven会隐式的把这些库间接依赖的库也加入到你的项目中。Maven也会处理这些依赖中的冲突,同时能让你自定义默认行为,或者排除一些特定的传递性依赖
站点生成和报告
Site生命周期只关心处理在src/site目录下的site内容,还有生成报告。在这个命令运行过之后,你将会在target/site目录下看到一个项目web站点。载入target/site/index.html你会看到项目站点的基本外貌。
1 | mvn site |
插件开发
Maven的核心是一个名为Plexus的反转控制(IoC)框架。
一个Maven插件是包含了一个插件描述符和一个或者多个Mojo的Maven构件。一个Mojo可以被认为是Maven中的一个目标,每一个目标对应了一个Mojo。
POM定义
1 | <packaging>maven-plugin</packaging> |
实现Mojo接口
继承AbstractMojo,实现一个目标接口。
Mojo中常用注解
1 | @goal <goalName> |
Mojo失败
MojoExecutionException:一个致命的异常,发生了一些不可恢复的错误。构建需要终止。
MojoFailureException:Maven遇到项目失败的时候,他会提供不同的“弹性”设置。mvn -ff mvn -fae mvn -fn等等。
面向模式的软件架构-Pipes and Filters模式
架构模式Pipes and Filters提供的结构适合用于处理数据流的系统。每个处理步骤封装在一个过滤器组件中,数据通过相邻过滤器之间的管道传输。通过重组过滤器,可打造多个相关的系统组。
背景
处理数据流
问题
假设要打造的系统必须对输入数据进行处理或转换,以单个组件的方式实现这种系统可能不可行,原因有多个:系统必须由多名开发人员打造;整个系统要完成的任务分多个处理阶段;需求很可能发生变化。
因此,需要提供灵活性,以便能够更换处理步骤或调整处理顺序。提供这样的灵活性后,便可使用既有处理组件打造出一些列系统。设计系统时,必须考虑如下作用力。
- 以后可通过更换或重组处理步骤来改进系统。
- 相比大型组件,在其他环境中重用小型处理步骤更容易。
- 不相邻的处理步骤不共享消息。
- 存在不同的输入数据源,如网络连接和提供温度的硬件传感器。
- 最终结果能够以各种方式和存储。
- 如果要求用户将中间结果存储到文件中,供以后进一步处理,将很容易出错。
- 应避免同时执行多个处理步骤,如并行或半并行地执行这些步骤。
解决方案
架构模式Pipes and Filters将系统面临的任务分为多个一次执行的处理步骤。这些步骤通过在系统中传输的数据相关联:一个步骤的输出是下一个步骤的输入。每个处理步骤都由过滤器组件实现。过滤器一边使用数据一边提供数据,而不是等到获得所有输入后才生成输出。这降低了延迟,实现了真正的并行处理。数据源、过滤器、数据接收器通过管道依次相连,每条管道都在相邻处理步骤之间传输数据。
结构
过滤器组件式流水线的处理单元,负责充实、提炼或转换输入数据。
过滤器操作可由多种事件触发:
- 下一个流水线元素从过滤器拉去输出。(被动过滤器)
- 前一个流水线元素向过滤器推送输入。(被动过滤器)
- 过滤器不间断地循环,从流水线上游拉去输入,并向下游推送输出。(主动过滤器)
管道指的是过滤器之间、数据源和第一个过滤器之间以及最后一个过滤器和数据接收器之间的连接。将两个主动过滤器组件相连时,管道负责让它们同步,这是使用先进先出缓冲区实现的。被动过滤器与主动过滤器相连时,可这样实现管道:由主动过滤器直接调用被动过滤器,但直接调用增加了重组过滤器的难度。
数据源指的是系统输入,提供结构或类型相同的数据序列,如包含文本行的文件或提供数字序列的传感器。
数据接收器从流水线末端收集结果,分主动接收器和被动数据接收器。
实现
Pipes and Filters架构实现起来很简单。可将系统服务(如消息队列或UNIX管道)用作管道连接,也可采取其他方式,如直接调用。
- 将系统要完成的任务划分为一系列处理阶段。
- 定义沿管道传递的数据的格式。
- 确定如何实现每条管道连接。
- 设计并实现过滤器。
- 实际错误处理机制。UNIX为错误消息定义了专用输出通道stderr。过滤器并行运行时,单个错误通道可能包含来自不同组件的错误消息,这即不明显又无法预测。
- 搭建处理流水线。
效果
优点:
- 不需要中间文件,但也可以使用。
- 可更换过滤器。
- 可重组过滤器
- 可重用过滤器组件
- 可快速创建流水线原型。
- 效率因并行处理得以提高。
缺点:
- 共享状态信息的开销高昂或缺乏灵活性。
- 通过并行处理提高效率的初衷常常成为泡影。
- 数据转换开销。
- 错误处理。