`
iamweiming
  • 浏览: 39075 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

easymock浅析

 
阅读更多

一、EASYMOCK基本工作方式回顾
首先我们通过一个最基本的例子来回顾一下EASYMOCK的工作方式

我们有一个计算器,里面依赖一个做int加法的加法器
Calculator.java

Java代码  收藏代码
  1. public   class  Calculator {  
  2.       
  3.     private  Adder adder;  
  4.   
  5.     public   void  setAdder(Adder adder) {  
  6.       
  7.         this .adder = adder;  
  8.     }  
  9.       
  10.     public   int  add( int  x,  int  y){  
  11.         return  adder.add(x, y);  
  12.     }  
  13.   
  14. }  



Adder.java

Java代码  收藏代码
  1. public   interface  Adder {  
  2.       
  3.     public   int  add( int  x,  int  y);  
  4. }  



其中这个加法器的实现在别的模块中
在计算器的模块中我们调用加法器的接口

现在我们需要对Calculator进行单元测试
此时我们不想依赖Adder的具体实现来进行测试
这样Adder模块内的错误将会干扰Calculator模块的测试
造成问题定位困难

此时我们需要一个Adder接口的Mock对象
由它来响应Calculator的方法调用
这里我们可以实用EASYMOCK所提供的功能

Java代码  收藏代码
  1. import  org.easymock.EasyMock;  
  2. import  org.junit.Before;  
  3. import  org.junit.Test;  
  4.   
  5. import  junit.framework.Assert;  
  6. import  junit.framework.TestCase;  
  7.   
  8. public   class  CalculatorTest  extends  TestCase {  
  9.   
  10.     private  Calculator tested;  
  11.   
  12.     private  Adder adder;  
  13.   
  14.     @Before   
  15.     public   void  setUp() {  
  16.   
  17.         tested = new  Calculator();  
  18.         adder = EasyMock.createMock(Adder.class );  
  19.         tested.setAdder(adder);  
  20.     }  
  21.   
  22.     @Test   
  23.     public   void  testAdd() {  
  24.         // adder in record state   
  25.         EasyMock.expect(adder.add(1 2 )).andReturn( 3 );  
  26.         EasyMock.replay(adder);  
  27.         // adder in replay state   
  28.         Assert.assertEquals(3 , tested.add( 1 2 ));  
  29.     }  
  30. }  


在setUp()中我们通过EasyMock.createMock()方法生成了一个MOCK对象
并且注入到Calculator的实例中
现在MOCK对象处于Record State下
在这个状态下,通过对MOCK对象进行方法调用
以及对EasyMock.expect() /andReturn() / andThrow()
方法的调用,我们能够记录MOCK对象的预期行为
这些行为将在Replay State中进行回放

比如上例,我们在adder对象的Record State下记录了一次
adder.add()调用,参数为1和2,返回值为3
接着在Replay State下,通过调用Calculator.add()方法
我们调用了adder对象的add()方法
EasyMock将会检查这次调用的方法和参数列表是否与已经保存的调用一致
如果一致的话,返回所保存的返回值
由于这次调用和Record State中的记录一致
上面例子中我们的test.add()将会返回3

二、MOCK对象的创建------JDK的动态代理
接下来我想通过EasyMock的源码来窥探一下Mock对象的创建过程
这条语句
adder = EasyMock.createMock(Adder.class);

调用了
in org.easymock.EasyMock

Java代码  收藏代码
  1. public   static  <T> T createMock(Class<T> toMock) {  
  2.     return  createControl().createMock(toMock);  
  3. }  


我们可以看到这里创建了一个控制器MocksControl
然后调用了MocksControl的createMock方法
in org.easymock.EasyMock

Java代码  收藏代码
  1. public   static  IMocksControl createControl() {  
  2.     return   new  MocksControl(MocksControl.MockType.DEFAULT);  
  3. }  


这个MocksControl类用于管理Mock对象的状态迁移
即Record State和Replay State的转换

我们再来看看MocksControl的createMock方法
in org.easymock.internal.MocksControl

Java代码  收藏代码
  1. public  <T> T createMock(Class<T> toMock) {  
  2.     try  {  
  3.         state.assertRecordState();  
  4.         IProxyFactory<T> proxyFactory = createProxyFactory(toMock);  
  5.         return  proxyFactory.createProxy(toMock,  new  ObjectMethodsFilter(  
  6.                 toMock, new  MockInvocationHandler( this ),  null ));  
  7.     } catch  (RuntimeExceptionWrapper e) {  
  8.         throw  (RuntimeException) e.getRuntimeException().fillInStackTrace();  
  9.     }  
  10. }  
  11.   
  12. protected  <T> IProxyFactory<T> createProxyFactory(Class<T> toMock) {  
  13.     return   new  JavaProxyFactory<T>();  
  14. }  


我们看到这里创建了一个代理类工厂
然后使用代理类工厂创建了Mock对象

接着来看一下JavaProxyFactory类的实现

Java代码  收藏代码
  1. public   class  JavaProxyFactory<T>  implements  IProxyFactory<T> {  
  2.     @SuppressWarnings ( "unchecked" )  
  3.     public  T createProxy(Class<T> toMock, InvocationHandler handler) {  
  4.         return  (T) Proxy.newProxyInstance(toMock.getClassLoader(),  
  5.                 new  Class[] { toMock }, handler);  
  6.     }  
  7. }  



这里使用了JDK中的java.lang.reflect.Proxy类来实现动态代理类的创建
-------------------------------------------------------
关于JDK的动态代理这里补充一个简单的例子给不太熟悉的同学
就从我们开始的Adder接口说起
补充一个实现类

Java代码  收藏代码
  1. public   class  AdderImpl  implements  Adder{  
  2.       
  3.     public   int  add( int  x,  int  y){  
  4.         return  x + y;  
  5.     }  
  6. }  


现在我想实现一个DEBUG功能,就是在执行add()方法的时候
能在控制台输出一行 "1 + 2 = 3"
使用Proxy模式,我们可以这样实现

Java代码  收藏代码
  1. public   class  AdderDebugProxy  implements  Adder{  
  2.       
  3.     private  Adder delegate;  
  4.       
  5.     public  AdderDebugProxy(Adder delegate){  
  6.         this .delegate = delegate;  
  7.     }  
  8.       
  9.     public   int  add( int  x,  int  y){  
  10.         int  result = delegate.add(x, y);  
  11.         System.out.println(""  + x +  " + "  + y +  " = "  + result);  
  12.         return  result;  
  13.     }  
  14. }  


Java代码  收藏代码
  1. public   static   void  main(String[] args) {  
  2.     Adder adder = new  AdderDebugProxy( new  AdderImpl());  
  3.     adder.add(1 , 2 );  
  4. }  



程序输出
1 + 2 = 3

但是这是一个静态代理,我们的代理类必须要静态写死
如果需要在程序运行时再生成代理类的话,就要使用JDK的动态代理功能
由于无法直接定义代理类,我们需要借助一个
java.lang.reflect.InvocationHandler
来定义我们需要的代理行为

Java代码  收藏代码
  1. public   class  DebugInvocationHandler  implements  InvocationHandler {  
  2.   
  3.     private  Adder delegate;  
  4.   
  5.     public  DebugInvocationHandler(Adder delegate) {  
  6.   
  7.         this .delegate = delegate;  
  8.     }  
  9.   
  10.     public  Object invoke(Object proxy, Method method, Object[] args)  
  11.             throws  Throwable {  
  12.   
  13.         try {  
  14.             if (method.getName().equals( "add" )){  
  15.                 if ( null  == args || args.length !=  2 ){  
  16.                     throw   new  IllegalArgumentException( "wrong argument length for add()" );  
  17.                 }  
  18.                 Integer x = (Integer)args[0 ];  
  19.                 Integer y = (Integer)args[1 ];  
  20.                 Integer result = delegate.add(x.intValue(), y.intValue());  
  21.                 System.out.println(""  + x +  " + "  + y +  " = "  + result);  
  22.                 return  result.intValue();  
  23.             }  
  24.             return  method.invoke(delegate, args);  
  25.         } catch (InvocationTargetException e){  
  26.             throw  e;  
  27.         }  
  28.     }  
  29. }  



在实际使用的时候,对于动态生成的Proxy类
调用proxy.add(int x, int y)
将会被封装成对InvocationHandler的调用
invoke(proxy, method, args)
其中method为add方法
args为封装x和y的Object数组

最后我们使用一个工厂类来创建它

Java代码  收藏代码
  1. public   class  AdderProxyFactory {  
  2.   
  3.     public   static  Adder createDebugProxy(Adder delegate) {  
  4.   
  5.         return  (Adder) Proxy.newProxyInstance(delegate.getClass()  
  6.                 .getClassLoader(), delegate.getClass().getInterfaces(),  
  7.                 new  DebugInvocationHandler(delegate));  
  8.     }  
  9. }  


Java代码  收藏代码
  1. public   static   void  main(String[] args) {  
  2.     Adder adder = AdderProxyFactory.createDebugProxy(new  AdderImpl());  
  3.     adder.add(1 2 );  
  4. }  


程序输出
1 + 2 = 3

-------------------------------------------------------

我们回过头来看EasyMock的源码

Java代码  收藏代码
  1. return  proxyFactory.createProxy(toMock,  new  ObjectMethodsFilter(  
  2.         toMock, new  MockInvocationHandler( this ),  null ));  


可以看到传入了两个参数,一个被MOCK的接口
一个ObjectMethodsFilter
这个ObjectMethodsFilter正如其名
做了一个中间层,起到了过滤Object中3个方法
equals() toString() hashCode()的作用
实际起作用的是MockInvocationHandler
而传入的this参数则间接的将MocksControl的引用传给了它所创建的MOCK对象

in org.easymock.internal.MockInvocationHandler

Java代码  收藏代码
  1. public   final   class  MockInvocationHandler  implements  InvocationHandler, Serializable {  
  2.   
  3.     private   static   final   long  serialVersionUID = -7799769066534714634L;  
  4.       
  5.     private   final  MocksControl control;  
  6.   
  7.     public  MockInvocationHandler(MocksControl control) {  
  8.         this .control = control;  
  9.     }  
  10.   
  11.     public  Object invoke(Object proxy, Method method, Object[] args)  
  12.             throws  Throwable {  
  13.         try  {  
  14.             if  (control.getState()  instanceof  RecordState) {  
  15.                 LastControl.reportLastControl(control);  
  16.             }  
  17.             return  control.getState().invoke(  
  18.                     new  Invocation(proxy, method, args));  
  19.         } catch  (RuntimeExceptionWrapper e) {  
  20.             throw  e.getRuntimeException().fillInStackTrace();  
  21.         } catch  (AssertionErrorWrapper e) {  
  22.             throw  e.getAssertionError().fillInStackTrace();  
  23.         } catch  (ThrowableWrapper t) {  
  24.             throw  t.getThrowable().fillInStackTrace();  
  25.         }  
  26.     }  
  27.   
  28.     public  MocksControl getControl() {  
  29.         return  control;  
  30.     }  
  31. }  




三、浅析MOCK对象的状态机制-----State模式的应用
我们直接看上面的代码,可以看到对MOCK对象的方法调用
直接被转化成了control.getState().invoke()的调用
这又是怎样的实现,我们回过头来看MocksControl的代码

Java代码  收藏代码
  1. public   class  MocksControl  implements  IMocksControl, IExpectationSetters<Object>, Serializable {  
  2.   
  3.     // .......       
  4.   
  5.     private  IMocksControlState state;  
  6.   
  7.     private  IMocksBehavior behavior;  
  8.   
  9.     // .......   
  10.   
  11.     public   final   void  reset() {  
  12.         behavior = new  MocksBehavior(type == MockType.NICE);  
  13.         behavior.checkOrder(type == MockType.STRICT);  
  14.         behavior.makeThreadSafe(false );  
  15.         state = new  RecordState(behavior);  
  16.         LastControl.reportLastControl(null );  
  17.     }  
  18.   
  19.     // ......   
  20.   
  21.     public   void  replay() {  
  22.         try  {  
  23.             state.replay();  
  24.             state = new  ReplayState(behavior);  
  25.             LastControl.reportLastControl(null );  
  26.         } catch  (RuntimeExceptionWrapper e) {  
  27.             throw  (RuntimeException) e.getRuntimeException().fillInStackTrace();  
  28.         }  
  29.     }  
  30.   
  31.     // ......   
  32.   
  33.     public  IExpectationSetters<Object> andReturn(Object value) {  
  34.         try  {  
  35.             state.andReturn(value);  
  36.             return   this ;  
  37.         } catch  (RuntimeExceptionWrapper e) {  
  38.             throw  (RuntimeException) e.getRuntimeException().fillInStackTrace();  
  39.         }  
  40.     }  


可以看到这是一个State模式的应用
MocksControl中保存了一个IMocksControlState的实例对象
IMocksControlState接口有两个实现类,正是RecordState和ReplayState,定义了不同的操作
而MocksControl类使用reset()和replay()实现状态的迁移
而一些其他操作,则由MocksControl对外提供接口,交由State实现

而IMocksBehavior则是MockControl对象的数据模型
保存了RecordState中储存的调用
以供ReplayState取用

四、浅析EASYMOCK的数据模型
接着我们进入MocksBehavior看一看EasyMock的数据模型
一路找下去有很多的层次,最后找到几个核心类:
org.easymock.internal.ExpectedInvocation 
org.easymock.internal.Invocation
org.easymock.IArgumentMatcher
org.easymock.internal.Result

首先我们来看Invocation类
in org.easymock.internal.Invocation

Java代码  收藏代码
  1. private   final  Object mock;  
  2.   
  3. private   transient  Method method;  
  4.   
  5. private   final  Object[] arguments;  


这个类有3个属性:MOCK对象、函数和参数列表,用于保存一次对MOCK对象的调用信息
在MockInvocationHandler中,方法调用被包含为一个Invocation类的参数传给
State对象的invoke()方法

Java代码  收藏代码
  1. return  control.getState().invoke(  
  2.         new  Invocation(proxy, method, args));  



接着来看ExpectedInvocation类
in org.easymock.internal.ExpectedInvocation

Java代码  收藏代码
  1. private   final  Invocation invocation;  
  2.   
  3. // ......   
  4.   
  5. private   final  List<IArgumentMatcher> matchers;  


这个类保存了一个调用信息和一系列ArgumentMatcher
这就是在RecordState中保存的调用信息
在ReplayState中MOCK对象接受方法调用
将会产生一个actual的Invocation对象
利用ExpectedInvocation类的matches()方法,EASYMOCK将匹配这个actual对象和原来记录的调用对象
in org.easymock.internal.ExpectedInvocation

Java代码  收藏代码
  1. public   boolean  matches(Invocation actual) {  
  2.     return  matchers !=  null  ?  this .invocation.getMock().equals(  
  3.             actual.getMock())  
  4.             && this .invocation.getMethod().equals(actual.getMethod())  
  5.             && matches(actual.getArguments()) : this .invocation.matches(  
  6.             actual, matcher);  
  7. }  


在这个函数中,matchers被用来比较两个调用的参数列表

默认的Matcher为org.easymock.internal.matchers.Equals
这个Matcher使用equals()方法来比较两个参数
在这个包下,EasyMock还定义了很多Matcher给使用者方便的使用
如果用户觉得不够够用的话,还可以自己来实现IArgumentMatcher

Result类实现了IAnswer接口,用来表示函数调用的返回(正常返回值或者异常抛出)
其内部有两个工厂方法分别用来创建ThrowingAnswer和ReturningAnswer
in org.easymock.internal.Result

Java代码  收藏代码
  1. private  IAnswer<?> value;  
  2.   
  3. private  Result(IAnswer<?> value) {  
  4.     this .value = value;  
  5. }  
  6.   
  7. public   static  Result createThrowResult( final  Throwable throwable) {  
  8.     class  ThrowingAnswer  implements  IAnswer<Object>, Serializable {  
  9.   
  10.         private   static   final   long  serialVersionUID = -332797751209289222L;  
  11.   
  12.         public  Object answer()  throws  Throwable {  
  13.             throw  throwable;  
  14.         }  
  15.   
  16.         @Override   
  17.         public  String toString() {  
  18.             return   "Answer throwing "  + throwable;  
  19.         }  
  20.     }  
  21.     return   new  Result( new  ThrowingAnswer());  
  22. }  
  23.   
  24. public   static  Result createReturnResult( final  Object value) {  
  25.     class  ReturningAnswer  implements  IAnswer<Object>, Serializable {  
  26.   
  27.         private   static   final   long  serialVersionUID = 6973893913593916866L;  
  28.           
  29.         public  Object answer()  throws  Throwable {  
  30.             return  value;  
  31.         }  
  32.           
  33.         @Override   
  34.         public  String toString() {  
  35.             return   "Answer returning "  + value;  
  36.         }  
  37.     }  
  38.     return   new  Result( new  ReturningAnswer());  
  39. }  
  40.   
  41. // .......   
  42.   
  43. public  Object answer()  throws  Throwable {  
  44.     return  value.answer();  
  45. }  


这就是在RecordState中使用andReturn()和andThrow()方法将会保存的信息
在ReplayState中,Result将会被取出,其answer()方法被调用
in org.easymock.internal.ReplayState

Java代码  收藏代码
  1. private  Object invokeInner(Invocation invocation)  throws  Throwable {  
  2.     Result result = behavior.addActual(invocation);  
  3.     LastControl.pushCurrentArguments(invocation.getArguments());  
  4.     try  {  
  5.         try  {  
  6.             return  result.answer();  
  7.         } catch  (Throwable t) {  
  8.             throw   new  ThrowableWrapper(t);  
  9.         }  
  10.     } finally  {  
  11.         LastControl.popCurrentArguments();  
  12.     }  
  13. }  


Mock对象则会返回我们需要的值,或者抛出我们需要的异常

五、EASYMOCK Class extension的MOCK对象创建-----CGLIB动态代理
继续回顾EASYMOCK的使用
如果我们的Adder做一个小修改,现在不是接口了,是实现类或者虚基类
那么org.easymock.EasyMock.createMock()就不能使用了
因为JDK的动态代理不能生成具体类的代理
这里就需要使用org.easymock.classextension.EasyMock.createMock()来创建代理类
而这里面使用的方法就是CGLIB的Enhancer字节码增强

in org.easymock.classextension.EasyMock

Java代码  收藏代码
  1. public   static  <T> T createMock(Class<T> toMock) {  
  2.     return  createControl().createMock(toMock);  
  3. }  
  4.   
  5. // ......   
  6.   
  7. public   static  IMocksControl createControl() {  
  8.     return   new  MocksClassControl(MocksControl.MockType.DEFAULT);  
  9. }  



而MocksClassControl是MocksControl的子类
它继承了父类的createControl方法
in org.easymock.internal.MocksControl

Java代码  收藏代码
  1. public  <T> T createMock(Class<T> toMock) {  
  2.     try  {  
  3.         state.assertRecordState();  
  4.         IProxyFactory<T> proxyFactory = createProxyFactory(toMock);  
  5.         return  proxyFactory.createProxy(toMock,  new  ObjectMethodsFilter(  
  6.                 toMock, new  MockInvocationHandler( this ),  null ));  
  7.     } catch  (RuntimeExceptionWrapper e) {  
  8.         throw  (RuntimeException) e.getRuntimeException().fillInStackTrace();  
  9.     }  
  10. }  



但是Override了createProxyFactory()方法
in org.easymock.classextension.internal.MocksClassControl

Java代码  收藏代码
  1. @Override   
  2. protected  <T> IProxyFactory<T> createProxyFactory(Class<T> toMock) {  
  3.     if  (toMock.isInterface()) {  
  4.         return   super .createProxyFactory(toMock);  
  5.     }  
  6.     return   new  ClassProxyFactory<T>();  
  7. }  


对于实际的类,它返回ClassProxyFactory
而ClassProxyFactory正是使用了CGLIB来创建代理类
这里再附一个CGLIB的简单例子,在ClassProxyFactory也能找到相类似的Proxy创建代码

--------------------------------------------------------
使用用我们的AdderImpl具体类

Java代码  收藏代码
  1. public   class  DebugMethodIntercepter  implements  MethodInterceptor {  
  2.   
  3.     public  Object intercept(Object obj, Method method, Object[] args,  
  4.             MethodProxy proxy) throws  Throwable {  
  5.                 // 对proxy类的调用将会转化为对其父类的调用   
  6.         Object result = proxy.invokeSuper(obj, args);  
  7.         System.out.println(""  + (Integer) args[ 0 ] +  " + "  + (Integer) args[ 1 ]  
  8.                 + " = "  + (Integer) result);  
  9.         return  result;  
  10.     }  
  11.   
  12. }  


Java代码  收藏代码
  1. public   static   void  main(String[] args) {  
  2.     AdderImpl adder = createDebugProxy();  
  3.     adder.add(1 2 );  
  4. }  
  5.   
  6. public   static  AdderImpl createDebugProxy() {  
  7.     Enhancer enhancer = new  Enhancer();  
  8.     enhancer.setSuperclass(AdderImpl.class );  
  9.     enhancer.setCallback(new  DebugMethodIntercepter());  
  10.     return  (AdderImpl)enhancer.create();  
  11. }  



程序返回
1 + 2 = 3

 

转自:http://shlteater.iteye.com/blog/394191

分享到:
评论

相关推荐

    pypy3.6-v7.3.0rc1-aarch64.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    星闪技术介绍.pptx

    星闪技术介绍

    FAT32文件系统的数据隐写

    源代码

    pypy2.7-v7.0.0-linux32.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    ppt328328328

    328328328328

    全自动晶圆激光隐形切割设备,全球前10强生产商排名及市场份额.pdf

    QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。 邮箱:market@qyresearch.com

    人工智能python安装环境(Miniconda3-py39-)(pycharm-community-2021.3.2.exe)

    人工智能python安装环境(Miniconda3-py39-)(pycharm-community-2021.3.2.exe)

    钢铁是怎样炼成的ppt模板x.pptx

    钢铁是怎样炼成的ppt模板x.pptx

    工作汇报 年终总结82.pptx

    封面 标题:基于物联网的智能家居系统年度总结 报告人信息:[姓名]、[职位/角色]、[所属机构/公司] 日期:[具体日期] 目录 引言 年度工作回顾 系统进展与亮点 技术创新与应用 市场反馈与用户评价 存在问题与挑战 未来展望与计划 结束语与感谢 一、引言 简要介绍智能家居系统的重要性和发展趋势 回顾本年度的工作目标和重点 二、年度工作回顾 系统建设与维护 完成的项目与里程碑 系统稳定性与可靠性提升 团队建设与培训 团队成员构成与职责 培训与技能提升活动 合作伙伴与资源整合 与供应商、合作伙伴的合作情况 资源整合与利用 三、系统进展与亮点 功能扩展与优化 新增功能介绍与效果评估 现有功能的优化与改进 用户体验提升 界面设计与交互优化 用户反馈与改进措施 四、技术创新与应用 物联网技术的应用 传感器与通信技术的升级 大数据分析与应用 智能家居的智能化管理 自动化控制与节能策略 安全防护与预警系统 五、市场反馈与用户评价 市场反馈分析 市场需求与竞争态势 市场占有率与增长趋势 用户评价总结 用户满意度调查结果

    LabVIEW专栏一、编写单独vi

    LabVIEW专栏一、编写单独vi

    SQL/数据库查询语言

    SQL/数据库查询语言

    flutter 常用插件整理附加使用实例

    flutter 常用插件整理附加使用实例

    IMG_20240327_093721.jpg

    IMG_20240327_093721.jpg

    列车车厢重排问题.pdf

    列车车厢重排问题是一个著名的组合优化问题,也被称为火车车厢重排问题(Railway Carriage Shunting Problem),它的目标是通过尽可能少的操作将一列乱序的车厢重新排列成有序的顺序。 在这个问题中,我们有一列由1到n号标记的乱序车厢。初始时,所有的车厢都停在一条没有分叉的轨道上。现在我们要进行一系列操作来将车厢按照升序排列。每个操作可以将车厢从一段轨道移到另一端轨道的任意位置。而这些操作的目标是,经过一定的操作后,所有车厢按照升序排列。 这是一个经典的组合优化问题,可以使用多种算法来解决。其中一种常见的解法是使用贪心算法。

    pypy3.6-v7.3.1-s390x.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    简历-求职简历-word-文件-简历模版免费分享-应届生-高颜值简历模版-个人简历模版-简约大气-大学生在校生-求职-实习

    简历-求职简历-word-文件-简历模版免费分享-应届生-高颜值简历模版-个人简历模版-简约大气-大学生在校生-求职-实习 简历是展示个人经历、技能和能力的重要文档,以下是一个常见的简历格式和内容模板,供您参考: 简历格式: 头部信息:包括姓名、联系方式(电话号码、电子邮件等)、地址等个人基本信息。 求职目标(可选):简短描述您的求职意向和目标。 教育背景:列出您的教育经历,包括学校名称、所学专业、就读时间等。 工作经验:按时间顺序列出您的工作经历,包括公司名称、职位、工作时间、工作职责和成就等。 技能和能力:列出您的专业技能、语言能力、计算机技能等与职位相关的能力。 实习经验/项目经验(可选):如果您有相关实习或项目经验,可以列出相关信息。 获奖和荣誉(可选):列出您在学术、工作或其他领域获得的奖项和荣誉。 自我评价(可选):简要描述您的个人特点、能力和职业目标。 兴趣爱好(可选):列出您的兴趣爱好,展示您的多样性和个人素质。 参考人(可选):如果您有可提供推荐的人员,可以在简历中提供其联系信息。 简历内容模板: 姓名: 联系方式: 地址: 求职目标: (简短描述您的求职意

    pypy3.6-v7.3.3rc1-linux32.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    智能床新品618营销推广项目方案ss.pptx

    智能床新品618营销推广项目方案ss.pptx

    asp代码(软件工程)精品课程教学网站的设计与实现ASP+SQLSever2000

    asp代码《软件工程》精品课程教学网站的设计与实现 ASP+SQL Sever2000本资源系百度网盘分享地址

Global site tag (gtag.js) - Google Analytics