`
Norther
  • 浏览: 26386 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

简单,易于Mock,仅依赖Spring的Domain Model

阅读更多

声明:该贴并不讨论Domain Model对于企业应用是否有意义,JE已经讨论过很多了,有很多优秀的帖供参考,这里仅谈实现。

马丁大叔在《企业应用架构模式中》提出Domain Model(领域模型,领域对象,Domain Object)的概念后,我们发现这才是开发企业应用更OO的模式,以前的Transaction Script简直太土了。。。按照领域模型组织起来的面向对象语言代码,简直美极了,而ROR中的ActiveRecord就是一种Domain Model的实现,其威力大家也都见识过了。但是因为Java语言的特性的原因(例如静态类型),一直没有一种好的,优美的,简单的方式去实现,主要的难题就是领域对象和数据库逻辑的依赖关系(例如和Dao的依赖),大家关于这方面的尝试从来就没有断过,目前有以下几种主流的实现方式:

1 放弃IOC,在DomainObject里直接去获得Dao的Instance,例如在Domain Object里拿到Spring上下文,直接get所需要的Bean,这样的优势就是简单,缺点就是DomainObject与Dao的实现以及上下文的耦合,领域逻辑的执行依赖数据库,无法单元测试。有的观点认为利用内存数据库,这样比真正的访问数据库要快,要方便,认为mock dao没有意义,这个观点还持保留意见 :)

2 利用一些框架的特性去实现Dao或者数据库访问能力的注入,例如利用Hibernate Interceptor,在Domain Object持久化的时候注入,这个方式有个明显的缺点就是Domain Object的生命周期依赖特定环境,也就是说这个Domain Object必须是由Hibernate构造的,其才会被注入Dao。这点其实很郁闷,我们以前有个项目就是用这种方式,自己写了Hibernate Interceptor去注入Domain Object,初看很爽很EASY,但是表现层这边,DWR将用户提交的表单数据转换成DomainObject后,那个Dao当然就是NULL了,于是自己又写了个DWR的 Convertor,去实现注入,而这个Domain Object真的是很Rich,随后又要通过web serivce传输,又要在webservice框架里加拦截器实现注入,真的是很郁闷。

3 将Dao通过方法参数传入,优点就是简单,POJOS In Action里就提到了这种方式,缺点就是会影响方法以及接口的设计,经常会出现一个方法会有6个参数之多,而5个都是Dao,真是很丑陋。

4 利用AspectJ等工具实现编译期注入,Spring 2.0刚出的时候,引入了AspectJ,其Reference中就提到了这个方式,我也尝试过,非常的麻烦,而且性能很慢,没记错的话当时大概是06年10月份,用的JDK5,现在不知道有没有改善,我当时还在JE发过新手帖询问过,某大牛说是BEA的虚拟机对其有优化,我也没条件尝试,而且编译期的手脚动太多了,感觉不是很好。

上述几种方式都有明显的缺点,我们追求的好的领域模型的实现就是,领域模型不依赖实现,例如可以注入,易于单元测试,而且其生命周期不依赖特定的环境,还要够简单!难道真是Robbin所说的JAVA不适合Rich Domain Model吗?不见得:

首先,不依赖实现,可以注入,那么要在领域模型中提供接口dao的set方法,允许被注入,这个很明确。

其次,领域模型的生命周期不依赖环境, 不依赖hibernate的构造,不被spring管理等。一般的情况,dao都是无状态的,单例的,那么把领域模型中的依赖的dao设置为static,static是class级别的,和具体的instance没关系,只要在应用初始化的时候给所有的领域模型的class注入一次,整个应用运行的时间周期,领域模型随意怎么new,在哪new,new出来的Domain Object的都可以访问一开始被注入的static的dao,实现了领域模型的生命周期不依赖环境,这样也易于测试,易于MOCK,和目前传统编程方式差别很小,仅仅把dao改为static的。

 

但是spring可以实现对一个class的静态peroperty注入吗?答案是不能(经bottom同学修正,spring是可以静态property注入,但是还是要在所有bean初始化之前注入才行,所以还是要扩展,我在3楼已经回复说明),但是spring是个优美扩展性强的框架,我们只要稍微扩展一下,就可以达到我们的目的。

就拿一个典型的web应用为例,普遍的方式的是在web.xml配置spring的context loader listener和spring的配置文件,这个listener会在应用启动时,创建并初始化spring的上下文,我们需要在所有bean初始化之前注入domain class,那么就在这里找切入点,通过spring的源代码可以发现类AbstractApplicationContext的refresh方法内的finishBeanFactoryInitialization有些重要的东东。

/**
 * Finish the initialization of this context's bean factory,
 * initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
       // 省略
       // Instantiate all remaining (non-lazy-init) singletons.
        // 这里,会初始化所有的单例
       beanFactory.preInstantiateSingletons();
}


通过观察代码发现,初始化单例其实就是将Spring配置文件中定义的所有不是lazy init的单例初始化了,对一个Bean的初始化就是调用getBean来完成,Spring在调用getBean的时候,会判断如果已经初始化了就直接返回给你Bean的引用,否则就初始化这个Bean再返回给你,很典型的实现LAZY的方式。

我们只需要在这个初始化方法之前,主动去扫描所有的领域模型class,并且知道他们需要注入的Dao,直接getBean获得dao的实例,并且注入进Domain Model的class就完成了,那么我们只需要自己实现一个ConfigurableListableBeanFactory接口,也就是继承DefaultListableBeanFactory(其是ConfigurableListableBeanFactory接口的默认实现), 并且在preInstantiateSingletons方法的时候先注入领域模型,巨简单

private class StaticPropertyInjectSupportListableBeanFactory extends DefaultListableBeanFactory {
       @Override//覆盖父类的方法
       public void preInstantiateSingletons() throws BeansException {
              //先注入所有的领域模型的class
              injectStaticPropertyForBasePackage();
              //再调用父类的该方法去初始化所有的非LAZY单例
              super.preInstantiateSingletons();
       }
}

继续观察代码,finishBeanFactoryInitialization的方法的参数ConfigurableListableBeanFactory,是通过调用的createBeanFactory方法获得的,默认的实现在AbstractRefreshableApplicationContext中有这样一个方法,去创建DefaultListableBeanFactory,而这个方法是protected的,摆明着让我们去复盖扩展其功能,这也是spring易于扩展的体现,我们只要实现一个自己的ApplicaitonContext,覆盖createBeanFactory方法创建我们刚才自己实现的StaticPropertyInjectSupportListableBeanFactory就OK了,代码也巨简单

public class StaticPropertyInjectSupportXmlWebApplicationContext extends XmlWebApplicationContext {
       @Override
       protected DefaultListableBeanFactory createBeanFactory() {
              return new StaticPropertyInjectSupportListableBeanFactory(getInternalParentBeanFactory());
       }
}


实现了自己的ApplicationContext,怎么让Spring启动的时候初始化你的上下文呢?当然可以了,spring提供了扩展的后门,只要在web.xml配置context parameter "contextClass"告诉spring,他就会用你自己的ApplicationContext去初始化了,不配置该参数默认的ApplicationContext实现是XmlWebApplicationContext。

关于自己实现静态property的注入就很简单了,在context parameter里加入个参数configurableBeanBasePackage,获得所有要注入的领域模型的父包,就可以了,如下

<!--加入这两个参数,其余不变 -->
<!--告诉spring用我们自己实现的支持静态bean注入的context -->
<context-param>
        <param-name>contextClass</param-name>
	<param-value>com.norther.sps.StaticPropertyInjectSupportXmlWebApplicationContext</param-value>
</context-param>
<!--注入com.norther包下(包含子包)所有的领域模型的class-->
<context-param>  
	<param-name>configurableBeanBasePackage</param-name>
	<param-value>com.norther</param-value>
</context-param>


这样就OK了,有人会想到这样还是有一点点点点麻烦,用PostProcessorBean或者Spring的容器初始化完毕的事件去实现注入static property不是更爽吗?这也是我最先想到的方式,但是这两种方式的注入的时间太靠后了,那个preInstantiateSingletons方法 已经被调用过了,有些bean已经被初始化了,例如quartz的SchedulerFactoryBean,就有可能你部署的quartz或者其他的 job已经开始跑了,但是领域模型还没有被注入,在那些job里调用领域模型都会空指针,所以一定要在preInstantiateSingletons方法调用前去注入。

 

那么如何辨别哪些是领域模型呢?就利用了spring的Configurable这个annotation去标识这个是需要静态注入的领域模型,然后自己实现了两种自动装配,BY_TYPE和BY_NAME,以及JSR-250的Resource annotation,例子如下

@Configurable
public class Student {
       private Long id;

       @Resource //注入field的名字所代表的bean,
       private static StudentDao studentDao;

       public static void setStudentDao(StudentDao studentDao) {
              Student.studentDao = studentDao;
       }

 

@Configurable
public class Assistant {
       private static StudentDao studentDao;

       @Resource(name = "studentDao") //注入name为studentDao的bean 
       public static void setDao(StudentDao studentDao) {
              Assistant.studentDao = studentDao;
       }
@Configurable(autowire = Autowire.BY_TYPE)//自动装配
public class SchoolMaster {
       private static StudentDao studentDao;

       // by type
       public static void setDao(StudentDao studentDao) {
              SchoolMaster.studentDao = studentDao;
       }

单元测试以及Mock

@Configurable(autowire = Autowire.BY_NAME)//自动装配
public class Teacher {
       private static StudentDao studentDao;

       public static void setStudentDao(StudentDao studentDao) {
              Teacher.studentDao = studentDao;
       }

       public List<Students> getStudents() {
              return studentDao.getByClassId(this.getClassId());
       }

 

 

 StudentDao studentDao = EasyMock.createMock(StudentDao.class);
 EasyMock.expected(studentDao.getByClassId(998877)).andReturn(students);

 Teacher.setStudentDao(studentDao);
 Teacher zhang3 = new Teacher();
 zhang3.setClassId(998877);//设置班级ID
 List<Student> actual = zhang3.getStudents();
 .......


后半部分说了这么多其实都是实现static property的注入,原理很简单,代码也很简单,就4个类,但是我认为这么简单的东西spring之类的框架可以更容易并且实现的更好,也就是说目前的java可以简单的解决领域对象和dao依赖的问题从而实现rich domain model。

 

当然这个代码只是demo性质的,实现的比较简单,只有web方式的实现方法,希望和大家共同讨论。

 

  • spis.zip (12.3 KB)
  • 描述: 源代码,依赖spring 2.5,servlet-api,log4j,测试部分依赖EasyMock,Junit
  • 下载次数: 241
分享到:
评论
27 楼 Feiing 2008-05-17  
ajoo 写道
Feiing 写道
的确 在容器中要保证线程安全以及引用不被改变只能靠人为约定

在 test case 里 setup()  设置了, 别的线程不一定能看到正确的 static 引用是为啥  我还真不知道  莫非是你以前说过的 junit 的问题 ? 那个帖子我没有认真看

跟junit没关系。java的变量,只有volatile的和final的才保证线程间的可见性。



有相应的 spec 吗 ?

照我以前的理解 volatile 是为了保证并发写操作的线程安全, 而 final 是为了防止引用被改变, 如果一个 static 变量保证只被初始化一次, 也会有可见性问题吗 ?
26 楼 Norther 2008-05-17  
ajoo 写道
Norther 写道
ajoo 写道
Feiing 写道
呵呵 ajoo 别认真 , norther 也是想表达自己的想法, static 的确有不好的地方, 但是线程安全问题确是不存在的

这个存在不存在是要依赖上下文地。如果那个set仅仅是在系统初始化时候调用,以后都不再碰它(你怎么保证这一点?把它变成private么?还是放一个大写的DO NOT CALL ME!注释在那里?),而且初始化之后有一个同步动作,而且初始化过程中不会把那个static引用泄漏到别的线程,那么,确实,它是安全的。

不过即使如此,测试里面也不是安全的。假设一个测试setUp()里面调用了set,然后测试过程中某个地方启动了线程,那么这个线程能否看到正确地static引用都是不确定的。


呵呵,代码级别是没有办法控制,但是应用一个机制之前应该对它有一定了解,就像写servlet没人会把每次request的状态保存在instance里一样,是需要知道servlet是单例的,这个servlet也控制不了的。

测试只能小心点去给他set null,这确实不爽,或者写个扩展一下TestCase,每个test都用不同的classloader。


很多人对ww的request scope action有意见,我反而认为servlet规范的单例设计的不好。ww走的才是正确的革命路线。


完蛋了,这次观点一致了,我也认为WW的那样更合适,重新举个例子,但是应用一个机制之前应该对它有一定了解,就像用Hibernate的Lazy load,你的entity必须不能是final的,final不final,Hibernate也保证不了?: p
25 楼 ajoo 2008-05-17  
Norther 写道
ajoo 写道
Feiing 写道
呵呵 ajoo 别认真 , norther 也是想表达自己的想法, static 的确有不好的地方, 但是线程安全问题确是不存在的

这个存在不存在是要依赖上下文地。如果那个set仅仅是在系统初始化时候调用,以后都不再碰它(你怎么保证这一点?把它变成private么?还是放一个大写的DO NOT CALL ME!注释在那里?),而且初始化之后有一个同步动作,而且初始化过程中不会把那个static引用泄漏到别的线程,那么,确实,它是安全的。

不过即使如此,测试里面也不是安全的。假设一个测试setUp()里面调用了set,然后测试过程中某个地方启动了线程,那么这个线程能否看到正确地static引用都是不确定的。


呵呵,代码级别是没有办法控制,但是应用一个机制之前应该对它有一定了解,就像写servlet没人会把每次request的状态保存在instance里一样,是需要知道servlet是单例的,这个servlet也控制不了的。

测试只能小心点去给他set null,这确实不爽,或者写个扩展一下TestCase,每个test都用不同的classloader。


很多人对ww的request scope action有意见,我反而认为servlet规范的单例设计的不好。ww走的才是正确的革命路线。
24 楼 ajoo 2008-05-17  
Feiing 写道
的确 在容器中要保证线程安全以及引用不被改变只能靠人为约定

在 test case 里 setup()  设置了, 别的线程不一定能看到正确的 static 引用是为啥  我还真不知道  莫非是你以前说过的 junit 的问题 ? 那个帖子我没有认真看

跟junit没关系。java的变量,只有volatile的和final的才保证线程间的可见性。

23 楼 Norther 2008-05-17  
ajoo 写道
Feiing 写道
呵呵 ajoo 别认真 , norther 也是想表达自己的想法, static 的确有不好的地方, 但是线程安全问题确是不存在的

这个存在不存在是要依赖上下文地。如果那个set仅仅是在系统初始化时候调用,以后都不再碰它(你怎么保证这一点?把它变成private么?还是放一个大写的DO NOT CALL ME!注释在那里?),而且初始化之后有一个同步动作,而且初始化过程中不会把那个static引用泄漏到别的线程,那么,确实,它是安全的。

不过即使如此,测试里面也不是安全的。假设一个测试setUp()里面调用了set,然后测试过程中某个地方启动了线程,那么这个线程能否看到正确地static引用都是不确定的。


呵呵,代码级别是没有办法控制,但是应用一个机制之前应该对它有一定了解,就像写servlet没人会把每次request的状态保存在instance里一样,是需要知道servlet是单例的,这个servlet也控制不了的。

测试只能小心点去给他set null,这确实不爽,或者写个扩展一下TestCase,每个test都用不同的classloader。
22 楼 Feiing 2008-05-17  
的确 在容器中要保证线程安全以及引用不被改变只能靠人为约定

在 test case 里 setup()  设置了, 别的线程不一定能看到正确的 static 引用是为啥  我还真不知道  莫非是你以前说过的 junit 的问题 ? 那个帖子我没有认真看
21 楼 ajoo 2008-05-17  
Feiing 写道
呵呵 ajoo 别认真 , norther 也是想表达自己的想法, static 的确有不好的地方, 但是线程安全问题确是不存在的

这个存在不存在是要依赖上下文地。如果那个set仅仅是在系统初始化时候调用,以后都不再碰它(你怎么保证这一点?把它变成private么?还是放一个大写的DO NOT CALL ME!注释在那里?),而且初始化之后有一个同步动作,而且初始化过程中不会把那个static引用泄漏到别的线程,那么,确实,它是安全的。

不过即使如此,测试里面也不是安全的。假设一个测试setUp()里面调用了set,然后测试过程中某个地方启动了线程,那么这个线程能否看到正确地static引用都是不确定的。
20 楼 Feiing 2008-05-17  
呵呵 ajoo 别认真 , norther 也是想表达自己的想法, static 的确有不好的地方, 但是线程安全问题确是不存在的
19 楼 Norther 2008-05-17  
ajoo 写道
没,没看完。这都叫你看出来了?完了,没的混了!

那啥,俺投降了。static呀,挺好的,又容易测试,代码又漂亮,写着舒服,还容易懂,简直就是天才的创意呀!俺咋就想不到呢?唉,人和人就是不能比呀!佩服!


大侠。。别这样。。我很怕的。。。
18 楼 ajoo 2008-05-17  
没,没看完。这都叫你看出来了?完了,没的混了!

那啥,俺投降了。static呀,挺好的,又容易测试,代码又漂亮,写着舒服,还容易懂,简直就是天才的创意呀!俺咋就想不到呢?唉,人和人就是不能比呀!佩服!
17 楼 Norther 2008-05-17  
ajoo 写道
唉。要是有state monad该多好啊。不就是一个状态又不要显式传递,又要能够测试么?

static? gulp!

还有那个static setter都不考虑线程安全的?



不知道这位大侠看完我的帖子了吗?那个注入的动作整个应用只有一次,就是在所有BEAN初始化之前,注入的都是singleton,何来线程不安全?
16 楼 Norther 2008-05-17  
nihongye 写道
单元测试当然可以,不过没法mock dao接口,的确也不怎么好。
因为事实上,一个方法,我们还是可以将它分解,比如,准备数据那个阶段不管它,逻辑处理密集部分用另一protect方法,然后对逻辑密集部分进行单元测试。
我说不上静态有什么不好,纯属个人口味,因为我感觉它污染所有的实例。




感觉不好,这个。。。确实没什么办法,呵呵。
15 楼 ajoo 2008-05-17  
唉。要是有state monad该多好啊。不就是一个状态又不要显式传递,又要能够测试么?

static? gulp!

还有那个static setter都不考虑线程安全的?
14 楼 nihongye 2008-05-17  
单元测试当然可以,不过没法mock dao接口,的确也不怎么好。
因为事实上,一个方法,我们还是可以将它分解,比如,准备数据那个阶段不管它,逻辑处理密集部分用另一protect方法,然后对逻辑密集部分进行单元测试。
我说不上静态有什么不好,纯属个人口味,因为我感觉它污染所有的实例。


13 楼 Norther 2008-05-16  
nihongye 写道
static,这个妥协牺牲的也太巨大了。我宁愿让定义一个SessionHolder的接口。外部用ThreadLocal还是其它实现注入给domain都好。然后,domain里需要那个dao实现就自己new,如下:
public List findMyGirls(){
   MyGirlDAO myGirlDAO = new MyGirlDAO(session)
   myGirlDAO.find。。。
}
达到dao逻辑重用的目的



请问static有什么代价?代价大在哪?你这个方式就是我所说的第一种方式,无法单元测试。
12 楼 nihongye 2008-05-16  
static,这个妥协牺牲的也太巨大了。我宁愿让定义一个SessionHolder的接口。外部用ThreadLocal还是其它实现注入给domain都好。然后,domain里需要那个dao实现就自己new,如下:
public List findMyGirls(){
   MyGirlDAO myGirlDAO = new MyGirlDAO(session)
   myGirlDAO.find。。。
}
达到dao逻辑重用的目的
11 楼 Norther 2008-05-16  
jianfeng008cn 写道
static的话 数据库连接限制很大吧 针对同一个domain只能有一个db session了吧?

数据库没有任何问题,拿hibernate来说, 现在dao的session都是从TheadLocal去拿的,无所谓你的dao static不static,反正都是singleton的,没任何影响。
10 楼 Norther 2008-05-16  
jianfeng008cn 写道
Norther 写道
谢谢楼上同学的纠正,这个当然算静态property注入,但是,在bean的初始化的时候,每个bean初始化的时间是不确定的,仅spring管理的bean有依赖初始化的关系,而我们的domain model是class级别的注入,这个依赖关系,spring无法知道,而只有在这个MethodInvokingFactoryBean被spring初始化的时候才去注入,有可能这个时候,其他调用Domain Object的quartz的job已经再开始跑了,但是他们去执行一些domain object的方法时,他们都还没有被注入,所以必须要要在所有的bean初始化之前去注入,才能保证所有的spring管理的bean去调用domain object时,他们都是被注入过的。


既然你的调用是有依赖的 用spring配置了这些有依赖关系的bean 那么初始化的时候也肯定会根据这个关系生成实例,怎么会和你说的这样呢? 比方说 a 依赖于b 在实例化a的时候 发现b还没有实例化 那肯定会先去实例化b啊,即使lazy了 哪么用的时候肯定要实例化了吧


那肯定了,这是没办法的,用到的那些DAO等于都先初始化了。

在Spring中,你getBean("a"), 他会初始化a,如果a依赖b,spring就会初始化b,很明确。
但是这种情况下,你初始化一个quartz的任务,开始跑,他里面没有依赖a,只是他里面操作的领域模型Student,依赖a了,还是static的,Student不是被spring管理的,spring不会知道这个关系,它不会注入,也不会初始化a(如果他是lazy的)。
9 楼 jianfeng008cn 2008-05-16  
static的话 数据库连接限制很大吧 针对同一个domain只能有一个db session了吧?
8 楼 jianfeng008cn 2008-05-16  
moonranger 写道
你这个思路个人感觉的确不错,值得一试。但是仍有一个非常关键的问题没有解决,那就是事务问题。按照你这种方式实现的Rich Domain Object怎么配置事务呢?事务放在哪一层?

事物和原来差别不大吧 domain涵盖了service的功能而已

相关推荐

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    Grails案例

    - MVC架构:Grails遵循Model-View-Controller(MVC)设计模式,使应用程序的结构清晰,易于维护。 - GORM:Grails Object Relational Mapping(GORM)是Grails中的数据访问层,它提供了一个简单的方式来处理数据库...

    Grails权威指南

     11.3 使用spring进行依赖注入  11.3.1 使用grails中的bean  11.3.2 重载bean的定义  11.4 在grails中使用spring控制器  11.5 在grails中使用acegi  11.6 使用xfire创建soap服务  11.7 本...

    win7修复本地系统工具

    win7修复本地系统工具

    《自动化专业英语》04-Automatic-Detection-Block(自动检测模块).ppt

    《自动化专业英语》04-Automatic-Detection-Block(自动检测模块).ppt

    《计算机专业英语》chapter12-Intelligent-Transportation.ppt

    《计算机专业英语》chapter12-Intelligent-Transportation.ppt

    西门子S7-1200博图平台下3轴伺服螺丝机程序解析与应用

    内容概要:本文详细介绍了基于西门子S7-1200博图平台的3轴伺服螺丝机程序。该程序使用SCL语言编写,结合KTP700组态和TIA V14及以上版本,实现了对X、Y、Z三个轴的精密控制。文章首先概述了程序的整体架构,强调了其在自动化控制领域的高参考价值。接着深入探讨了关键代码片段,如轴初始化、运动控制以及主程序的设计思路。此外,还展示了如何通过KTP700组态实现人机交互,并分享了一些实用的操作技巧和技术细节,如状态机设计、HMI交互、异常处理等。 适用人群:从事自动化控制系统开发的技术人员,尤其是对西门子PLC编程感兴趣的工程师。 使用场景及目标:适用于希望深入了解西门子S7-1200博图平台及其SCL语言编程特点的学习者;旨在帮助读者掌握3轴伺服系统的具体实现方法,提高实际项目中的编程能力。 其他说明:文中提供的代码示例和设计理念不仅有助于理解和学习,还能直接应用于类似的实际工程项目中。

    MATLAB仿真:非线性滤波器在水下长基线定位(LBL)系统的应用与比较

    内容概要:本文详细探讨了五种非线性滤波器(卡尔曼滤波(KF)、扩展卡尔曼滤波(EKF)、无迹卡尔曼滤波(UKF)、粒子滤波(PF)和变维卡尔曼滤波(VDKF))在水下长基线定位(LBL)系统中的应用。通过对每种滤波器的具体实现进行MATLAB代码展示,分析了它们在不同条件下的优缺点。例如,KF适用于线性系统但在非线性环境中失效;EKF通过雅可比矩阵线性化处理非线性问题,但在剧烈机动时表现不佳;UKF利用sigma点处理非线性,精度较高但计算量大;PF采用蒙特卡罗方法,鲁棒性强但计算耗时;VDKF能够动态调整状态维度,适合信标数量变化的场景。 适合人群:从事水下机器人(AUV)导航研究的技术人员、研究生以及对非线性滤波感兴趣的科研工作者。 使用场景及目标:①理解各种非线性滤波器的工作原理及其在水下定位中的具体应用;②评估不同滤波器在特定条件下的性能,以便为实际项目选择合适的滤波器;③掌握MATLAB实现非线性滤波器的方法和技术。 其他说明:文中提供了详细的MATLAB代码片段,帮助读者更好地理解和实现这些滤波器。此外,还讨论了数值稳定性问题和一些实用技巧,如Cholesky分解失败的处理方法。

    VMware-workstation-full-14.1.3-9474260

    VMware-workstation-full-14.1.3-9474260

    DeepSeek系列-提示词工程和落地场景.pdf

    DeepSeek系列-提示词工程和落地场景.pdf

    javaSE阶段面试题

    javaSE阶段面试题

    《综合布线施工技术》第5章-综合布线工程测试.ppt

    《综合布线施工技术》第5章-综合布线工程测试.ppt

    安川机器人NX100使用说明书.pdf

    安川机器人NX100使用说明书.pdf

    S7-1200 PLC改造M7120平面磨床电气控制系统:IO分配、梯形图设计及组态画面实现

    内容概要:本文详细介绍了将M7120型平面磨床的传统继电器控制系统升级为基于西门子S7-1200 PLC的自动化控制系统的过程。主要内容涵盖IO分配、梯形图设计和组态画面实现。通过合理的IO分配,确保了系统的可靠性和可维护性;梯形图设计实现了主控制逻辑、砂轮升降控制和报警逻辑等功能;组态画面则提供了友好的人机交互界面,便于操作和监控。此次改造显著提高了设备的自动化水平、运行效率和可靠性,降低了维护成本。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和控制系统设计的专业人士。 使用场景及目标:适用于需要进行老旧设备升级改造的企业,旨在提高生产设备的自动化水平和可靠性,降低故障率和维护成本。具体应用场景包括但不限于金属加工行业中的平面磨床等设备的控制系统改造。 其他说明:文中还分享了一些实际调试中的经验和技巧,如急停逻辑的设计、信号抖动的处理方法等,有助于读者在类似项目中借鉴和应用。

    chromedriver-linux64-136.0.7103.48.zip

    chromedriver-linux64-136.0.7103.48.zip

    IMG_20250421_180507.jpg

    IMG_20250421_180507.jpg

    《网络营销策划实务》项目一-网络营销策划认知.ppt

    《网络营销策划实务》项目一-网络营销策划认知.ppt

    Lianantech_Security-Vulnerabil_1744433229.zip

    Lianantech_Security-Vulnerabil_1744433229

    MybatisCodeHelperNew2019.1-2023.1-3.4.1.zip

    MybatisCodeHelperNew2019.1-2023.1-3.4.1

Global site tag (gtag.js) - Google Analytics