2004年12月12日

This article discusses:








  • The role of Mock objects
  • How to acquire and use NMock
  • Overview of a shopping sample
  • Design guidelines for testability

This article uses the following technologies:
Testing, .NET, C#


Code download available at:
NMock.exe (137KB)



ave you ever considered implementing unit tests for a project but dismissed the idea because the module you wanted to test either had too many dependencies or it was so difficult to isolate the unit itself that the tests started to look like integration tests? Or perhaps setting up and configuring all the external dependencies was impractical or downright impossible to achieve within your time constraints or budgets?


Mock objects can help you overcome these types of obstacles and, as an added benefit, can help enforce good design practices. In this article, I will describe how to use a publicly available dynamic mock object library called NMock, and will provide you with some of the design guidelines that I have found to be of practical use in my own work.


NMock is a great tool, but unfortunately it doesn’t currently come with documentation or samples, so it’s difficult to figure out what to do with it, or even for what use it is intended. I will try to illustrate the purpose and use of NMock by showing you how to write unit tests for a business logic library which is dependent on a data access layer (DAL).


Understanding the Unit Testing Problem

Before we embark on our journey through the sample code I’ve prepared for this article, it’s necessary to understand the problem: unit testing libraries that have a complex set of dependencies. It is best to design libraries to have a minimal set of dependencies, but sometimes this is not practical, especially when the library has not been designed with testability in mind. When unit testing libraries with a complex set of dependencies, these dependencies may require difficult or time-consuming setup procedures which are detrimental to the test process. Consider the case of a business logic library which uses a data access component to communicate with a database server, as illustrated on the left side of Figure 1.


Figure 1 Mock Testing Concept
Figure 1 Mock Testing Concept

Although the binding between the business logic library and the data access component is interface-based, the business logic library still needs to be able to access properties and methods on the data access component during unit testing, and that’s where the trouble begins.


The data access component needs a connection to a database to work. This means that you need to configure the component to access the right database, and you almost certainly need to set up a test instance of the database so that you don’t risk corrupting a production database during testing. If you are using SQL Server 2000, this may be a fairly manageable task, but what if the data access component provides access to a mainframe-based database? In the latter case, setting up a test instance of the database may be impractical at best. And even when using SQL Server, time may be a prohibitive factor.


However, configuring the DAL for test is not the only problem in this scenario. If your data access component is part of your development project, it may not even exist yet, and even if it does, it most likely will still be under development with a number of bugs, both known and unknown. In this case, you risk possible unit test failure due to bugs in the DAL even if the code under test has no defects. In other words, in such a case the library under test would not be properly isolated from other modules, so at that point the test is actually no longer a unit test.


Mock Objects to the Rescue

Traditionally, unit testing terminology has included the concepts of drivers and stubs. During your career as a software developer, you have probably created quite a few of these drivers and stubs for each module you’ve needed to test.


A driver is a piece of software that is written with the sole purpose of accessing properties and methods on a library to test the functionality of that library. A stub is a piece of software whose only purpose is to provide the library under test with an implementation of any modules it may need to communicate with to perform its work.


Figure 2 Unit Testing Concepts and Implementation
Figure 2 Unit Testing Concepts and Implementation

As illustrated in Figure 2, this concept translates well into practical terms. With the tools available today, you have a framework for implementing both drivers and stubs in a dynamic and flexible way. As an implementation of a driver, I use NUnit (currently at version 2.2), and for stubs I use NMock (currently at version 1.1) to create dynamic mock objects at run time. If you refer back to the right side of the diagram in Figure 1, a mock object implementing the IMyDataAccess interface has replaced the intended production DAL. NMock can create such a mock object at run time.


It is worth noting that NMock uses reflection to create mock implementations of interfaces at run time, so to use NMock it is necessary to code against interfaces instead of implementations. As this is already a well-regarded best practice, NMock helps to enforce this important design technique.


The Shopping Sample Library

The download for NMock consists of an assembly that can be used right away; no installation or configuration is necessary (see http://www.nmock.org). To illustrate the use of NMock, I’ll show you how to write unit tests for a very simplified shopping basket library which uses a DAL. The library uses the DAL by accessing a data access object that implements the IShoppingDataAccess interface, which is shown here:

using System;

namespace NMockExample.Shopping
{
public interface IShoppingDataAccess
{
string GetProductName(int productID);
int GetUnitPrice(int productID);
BasketItem[] LoadBasketItems(Guid basketID);
void SaveBasketItems(Guid basketID, BasketItem[] basketItems);
}
}



The Basket class illustrated in Figure 3 contains functionality to perform operations such as calculating the subtotal, saving the basket to the data store, and adding a new item to the basket. You may have noticed that the constructor takes as a parameter an object that implements the IShoppingDataAccess interface. The class will use this object to perform data access operations. For more information on this pattern, which is known as “Inversion of Control,” see Martin Fowler’s article on the subject at Inversion of Control Containers and the Dependency Injection pattern.


The Basket class contains an internal list of BasketItem objects, which are shown in Figure 4. Given a product ID, a BasketItem object uses its internal data access object to provide product information such as unit price and product name.


As you can see, most operations in this Shopping sample library make use of data access methods, which means that I need a way to isolate the Shopping library from the DAL if I want to write meaningful unit tests—tests that truly reflect the correctness of the method under test and that are not affected by one of its dependencies and skewing the results.


NMock to the Rescue

All classes in the Shopping sample library expose a way to externally define the data access implementation they use, so we can use NMock to provide a dynamic mock implementation of IShoppingDataAccess to the Shopping classes.


Let’s start by looking at a very simple unit test which demonstrates how to create a mock instance of IShoppingDataAccess using NMock. Before writing any code, it is necessary to add a reference to NMock.dll and add a using statement for the NMock namespace. The following code is a very simple unit test of the Basket class’s Save method, though at the moment no assertions are being performed:

DynamicMock dataAccess = new DynamicMock(typeof(IShoppingDataAccess));
Basket b = new Basket((IShoppingDataAccess)dataAccess.MockInstance);
b.Save();


To create a mock object with NMock, instantiate a new DynamicMock object, passing to its constructor the type you want it to implement. The mock object itself doesn’t implement the type but is an object which contains information about the behavior of the mock object and can emit an implementation of the specified type. This is exactly what happens in the next line of code, where the Basket constructor is passed a reference to the DynamicMock object’s MockInstance property.


DynamicMock uses reflection to emit an implementation of the desired type and expose it through the MockInstance property—in this case an implementation of IShoppingDataAccess. As MockInstance returns an object of type System.Object, it is necessary to cast the returned reference to the desired type.


In this case, the dynamically created implementation of IShoppingDataAccess is passed to the Basket constructor, and when the Save method is called, the Basket object can call the SaveBasketItems method on its internal IShoppingDataAccess member. Nothing was specified in the setup code, so the DynamicMock object doesn’t know what to do with the call to SaveBasketItems. In this case, it doesn’t matter because the method returns void. As such, this particular unit test only really tests that the Basket constructor and the Save method can be executed without exceptions.


Although this is not the most exciting unit test, it already demonstrates how to dynamically create a mock implementation of an interface. Had I passed a null reference to the Basket constructor instead of the mock object, the code would have thrown an exception when the Save method of the Basket class was called.


Setting Up Results

The default behavior of a mock instance is to return null from every method call that returns a reference type and to return the default value from every method call that returns a value type, so using it in the fashion I just illustrated provides very limited usefulness. It may be adequate for void methods, but falls short when you need to test a method with a return value.


Happily, the DynamicMock class allows me to modify the behavior of its MockInstance property. Before I pass the mock instance to my test target, I can call some methods on the DynamicMock class, which will instruct the mock instance to behave as I need it to.


To illustrate this, I wrote a simple unit test to test the BasketItem constructor. If you examine the code for the constructor in Figure 4, you will notice that during construction, the BasketItem class’s ProductID property is being set, which results in two calls to the DAL. Both calls return a value, so if I had repeated my previous action, a NullReferenceException would have been thrown by the call to GetUnitPrice. When dealing with methods with return values, the default behavior of the mock instance isn’t sufficient anymore. It is necessary to instruct the mock object about what to do, as shown in Figure 5.


Note that after I’ve created the DynamicMock object, but before I begin using it in my target library, I use the SetupResult method to modify the behavior of the mock instance. I configure it so that every time it receives a call to the GetUnitPrice method of IShoppingDataAccess, it should return 99, and every time it receives a call to GetProductName, it should return “The Moon.” The SetupResult method is defined like this:

void SetupResult(System.String methodName,
System.Object returnVal, params System.Type[] argTypes)


The first two parameters supply the method name and return value, respectively. The next parameter is an array of types that will be used to identify which method was called. Why is this necessary? Imagine for a moment that there are overloaded versions of the GetUnitPrice method, one taking as a parameter an integer value, and the other taking a System.Guid value. If the parameter list wasn’t supplied to the DynamicMock object, it wouldn’t know which one of the two GetUnitPrice methods I meant. To uniquely identify the method, I need to supply DynamicMock with enough information to allow it to infer the method’s signature.


By calling SetupResult as I do here

SetupResult(“GetUnitPrice”, 99, typeof(int));
I indicate that when the mock instance receives a call to the GetUnitPrice method that takes a single integer argument, it should return a value of 99. This allows DynamicMock to uniquely identify the method in question.


The last three lines of the unit test are just standard NUnit assertions to test that the newly created BasketItem object contains the expected data, and that it calculates the correct price.


Setting Expectations

The SetupResult method is still a rather blunt instrument. Any call to the configured method is answered with the same return value, no matter what parameters are used in the method call. If I want to test that the Basket class calculates correct subtotals when containing different items, I can’t easily use the SetupResult method. If I tried to add two different items to the Basket object, both items would get the same unit price, as the call to GetUnitPrice would return whatever I defined with SetupResult.


To solve this problem, there are other methods in the DynamicMock class I can use, as illustrated in Figure 6. In this test, I add two different BasketItem objects to the Basket object. During object instantiation, each BasketItem object will call the GetUnitPrice and GetProductName methods to retrieve information from the DAL.


To configure the mock instance to return appropriate values for different parameter values, I use the ExpectAndReturn method. This method looks similar to the SetupResult method, but has a few important differences. The ExpectAndReturn method configures the mock instance to expect one call to the method, with the supplied parameter values. Instead of using parameter types to identify the target method, the method signature is inferred from the supplied parameter values.


When I use the following parameters

ExpectAndReturn(“GetUnitPrice”, 99, 1);
the mock instance will return 99 when the GetUnitPrice method is called with a parameter value of 1. If the parameter value is different from 1, a VerifyException is thrown by NMock. Additionally, since the code in Figure 6 defines the next expected call to GetUnitPrice to have a parameter value of 5, a VerifyException will be thrown if the method isn’t called with that parameter. Since the mock instance is told to expect a total of two calls to GetUnitPrice, any third call would also result in an exception, even in cases where the parameter is 1 or 5.


Let the Mock Participate in the Test

As you’ve probably already noticed, the ExpectAndReturn method is a lot less forgiving than the SetupResult method. In my opinion, this is a good thing because it allows you to add a whole new level of control to your tests.


Consider a test where you use SetupResult. Aside from the obvious disadvantage that all calls to the same method return the same value, you don’t get much validation out of the mock object. Regardless of whether your target library called a method zero, once, or many times, you wouldn’t notice the difference. Conversely, you can use ExpectAndReturn to validate that a method was called the expected number of times, with the expected parameters, and in the expected order. With this approach, a unit test not only has the opportunity to test that a module returns the correct values, but also that it communicates with any external dependencies as designed. In fact, with some modules where most of the implementation is internal, this may very well be the only way you can perform tests in any meaningful way.


Thus, letting the mock object itself participate in the test is a very good idea, so I’ll point out a few ways you can make NMock even less forgiving of the unexpected than its default settings allow.


If you take a good look at the unit test code in Figure 6, you may notice something odd: I didn’t set an expectation for a call to SaveBasketItems, and even though the Save method is called on the Basket class, the test succeeds. This happens because SaveBasketItems returns void, so NMock can more or less just ignore the call. You can (and should) change this behavior by turning on the Strict option of the DynamicMock class, like this:

dataAccess.Strict = true;
The Strict property defaults to false, but if you change it to true, any call which does not have an expectation set will cause a VerifyException to be thrown. In the case of the unit test in Figure 6, turning on the Strict option will suddenly cause the unit test to fail, as it should. However, in this case the unit test fails because the test code is faulty, not because there is a bug in the test target, so the test code should be corrected.


It may seem obvious to use ExpectAndReturn to set up an expectation for the call to SaveBasketItems, and in fact you can do that by specifying null as the return value. However, for setting up expectations for void methods, the Expect method is a more intuitive alternative. It is used and behaves just like ExpectAndReturn, except that it doesn’t take a parameter that defines a return value.


When setting up the expectation for SaveBasketItems, a problem manifests itself. The SaveBasketItems method takes as its first parameter a Guid, which is the ID of the Basket object. If you examine the definition of the Basket class in Figure 3, you will see that this ID is generated internally during object construction, but is not externally accessible (you may argue that this constitutes one of many design flaws of the Basket class, but it illustrates my point). Since I have no way of knowing the value of the basketID parameter in advance, how can I define the expectation? The answer lies in the NMock Constraint classes:

dataAccess.Expect(“SaveBasketItems”, new IsTypeOf(typeof(Guid)),
new BasketItem[]{item1, item2});


Instead of a Guid value, I use a new Constraint instance that instructs the mock object that any Guid value is acceptable. For the other parameter (the array of BasketItems), I still indicate the expected value, so although the basketID parameter is allowed to take any Guid value, the basketItems parameter is still as restricted as I can make it. In fact, NMock expectations always work with Constraint classes when setting parameter expectations. The IsEqual constraint is the default, which is why I haven’t demonstrated this syntax until now.


The explicit syntax

dataAccess.ExpectAndReturn(“GetUnitPrice”, 47, new IsEqual(5));
is equivalent to
dataAccess.ExpectAndReturn(“GetUnitPrice”, 47, 5);
Even when using the Strict option, you are not guaranteed that the mock object validates that all methods are called the expected number of times. In the case where a method is called too many times, NMock throws an exception the first time this happens, but it doesn’t automatically discover that a method was called fewer times than expected. Although it has no way of automatically knowing when the unit test is over, you can inform it manually:
dataAccess.Verify();


Calling the Verify method at the end of your unit test tells the mock object that the test is over and that it should verify that all members were accessed the expected number of times. If a member happened to be accessed fewer times than expected, a VerifyException would be thrown.


To summarize the steps thus far, you set the Strict option to true at the beginning, stick with the expectation-setting methods throughout, and call the Verify method at the end of the unit test. With very little effort, you’ll have added a whole new level of validation to your unit tests.


Designing for Testability

As I mentioned previously, I have designed my classes so that I can pass a reference to the data access object in the constructor. When using dynamic mocks, the mock is only defined at run time so it is necessary to provide some way for the dependent module to be assigned from outside the consuming module itself. Of course, this does not mean this is the only way. Usually, I overload the constructor so that I have one constructor in which dependencies can be assigned at run time and one in which these parameters are not included. The second type of constructor will usually become the default. For example, the Basket class could be instantiated in the following way:

Basket b = new Basket();


The constructors which explicitly assign dependent objects at run time would typically be used only for tests. The constructor where the assignment is hidden would be used in production code. Why this distinction?


With the decoupled approach used throughout this article, modules communicate with other classes via interfaces. An interface defines methods, properties, indexers, and events, but it doesn’t define constructors. This means that when a class needs to instantiate an interface implementation at run time, it has to make assumptions about the constructor.


In my opinion, the most reasonable assumption is to use the default parameterless constructor. Let’s imagine that I want to use a discount calculation object with my shopping basket and I have two different types to choose from: a data-based discount class and an algorithm-based class, as illustrated in Figure 7. The data-based class uses a database to look up discounts defined there, whereas the algorithm-based class uses some internal algorithm to calculate discounts. When instantiating an implementation of the IMyDiscount interface, it doesn’t make any sense to pass a reference to a data access module. The algorithm-based class doesn’t need it, and at design time I don’t know which of the two is going to be used. Using a constructor that passes a reference to a data access object would be making an unreasonable assumption about the implementation.


You may think that this is all very good, but then how can you define which interface implementation is going to be used at run time? The answer, of course, is by configuring the application. Consider a simple application configuration file for the Shopping library like the one shown here:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
<appSettings>
<add key=”IShoppingDataAccessType”
value=”NMockExample.DataAccess.ShoppingDataAccess, DataAccess” />
</appSettings>
</configuration>


In this configuration file, I define that for the implementation of the IShoppingDataAccess interface, my application should use the NMockExample.DataAccess.ShoppingDataAccess type, defined in the DataAccess assembly. The default constructor for the Basket class looks like this:

public Basket()
{
string typeName =
ConfigurationSettings.AppSettings["IShoppingDataAccessType"];
Type t = Type.GetType(typeName);
IShoppingDataAccess dataAccess =
(IShoppingDataAccess)Activator.CreateInstance(t);
Initialize(dataAccess);
}


As you can see, that’s all there is to it. You get the string defining the type from the configuration file, create the type by calling the static GetType method of the Type class, and create an instance of the type by calling the static CreateInstance method of the Activator class. The CreateInstance method returns an object of type System.Object, so you need to cast it to the type you need to make further use of it.


One problem with this approach is that these three lines of code can’t be unit tested with NMock. Either you need to create an empty stub assembly, or you need to test this code using your production implementation of the interface, which has all the problems I described at the beginning of the article. However, this is very simple code that always follows the same pattern: get the string from the configuration file, get the type using the string, and get an instance using the type. Chances are that if you have validated this code to work once, you shouldn’t have any more problems with it. Errors might occur within this code, but that should only happen if the string happens to define a type or an assembly which couldn’t be found, loaded, instantiated, or cast, or if there is a problem with the configuration file.


Figure 7 Two Discount Classes
Figure 7 Two Discount Classes

If you are still not convinced that this is a good way to define the interface implementation in production code, consider the alternative. In the example from Figure 7, if you don’t want to specify the implementation of IMyDiscount in the configuration file, you need to pass an instance of the implementation to the Shopping library at run time. In effect, this only defers the decision to the caller of the Shopping library. It is now the caller’s responsibility to figure out which interface implementation to use.


If the decision is made at design time, it means that you end up hardcoding the implementation that should be used, and you lose most of the advantages of an interface-based design.


If the decision is made at run time, it needs to involve code similar to what I just described. As a result, not much has been gained, but you have now moved the decision to a place far from the module where it is needed.


Consider a shopping app that contains a shopping basket, which may need to calculate discounts. Imagine that the Basket class has been extended a bit to implement an IBasket interface and to consume the IMyDiscount interface. The shopping app will instantiate and manipulate an IBasket object, which may or may not be an instance of the Basket class. If you want to use the Basket class, you need to pass it an IMyDiscount object. One implementation of IMyDiscount (the data-based discount module) needs a data access object, so you need to pass this object to the Basket class as well. Now you have not only made the assumption that you are going to use the Basket class, but also that the Basket class is going to use the data-based discount module. This cannot be changed without modifying the code of both the shopping application itself and the Basket class (depending on what you want to change).


Given all of these potential problems, I find the most practical course to be to let any module itself determine and instantiate the interface implementations it consumes.


Conclusion

In real-world software development projects, it is a rare library that doesn’t have some kind of external dependency with which it communicates frequently. When unit testing libraries, you need some kind of stub to simulate those external dependencies. NMock provides you with a valuable tool for achieving this with minimal effort. Although writing unit tests still involves work, NMock offers relief from writing, configuring, and deploying your own stub classes over and over again. Defining mock instances and their behaviors at run time also gives you a degree of flexibility and test validation you probably never would have been able to achieve if you wrote your own stubs for each external dependency.


Each time I found myself in a situation in which it seemed like NMock was holding me back, it turned out to be a good time to pause and rethink my design. Each time I discovered that by making my own design better, I could overcome my problem with unit testing and NMock as well. So NMock is not only a great tool for unit testing, it also forces you to make improvements to your own object-oriented code design.


2004年12月10日

我不小心将iis破坏了,重装后只能识别asp1.0 无法编写asp.net的程序(在visual stdio.net中新建web application都不行)我用visual stdio.net 2003的安装盘修复安装,还是打不开asp.net项目,原来忘了做一件事还需要运行如下的命令:

C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_regiis.exe -r




安装 Microsoft .NET Framework 软件开发工具包 (SDK) 或 Visual Studio .NET 之后,会创建 Internet 信息服务 (IIS) 映射,以便为 ASP.NET 的新文件扩展名及设置建立关联。如果在运行 SDK 或 Visual Studio 安装程序时没有安装 IIS,或是在运行 SDK 或 Visual Studio 安装程序之后卸载并重新安装了 IIS,那么这些设置将不正确。试图查看 ASP.NET 页时会遇到意外现象。

当您试图在 Visual Studio .NET 2003 中创建新的 ASP.NET Web 应用程序时,会收到以下错误信息:

Visual Studio .NET has detected that the specified Web server is not running ASP.NET version 1.1.You will be unable to run ASP.NET Web applications or services.

更多信息


要为 ASP.NET 修复 IIS 映射,请按照下列步骤操作:







1. 运行 Aspnet_regiis.exe 实用工具:










a. 单击“开始”,然后单击“运行”。
b. 在“打开”框中,键入 cmd,然后按 Enter 键。
c. 在命令提示符下键入以下命令,然后按 Enter 键:
“%windir%\Microsoft.NET\Framework\版本\aspnet_regiis.exe” -i
在此路径中,版本代表在服务器上安装的 .NET Framework 的版本号。键入此命令时,必须将此占位符替换为实际版本号。
2. 注册 Aspnet_isapi.dll:







a. 单击“开始”,然后单击“运行”。
b. 在“打开”文本框中,键入以下内容,然后按 Enter 键:
regsvr32 %windir%\Microsoft.NET\Framework\版本\aspnet_isapi.dll
Regsvr32 将返回注册结果。

2004年12月05日


其实,就算用Java建造一个不是很烦琐的web应用,也不是件轻松的事情。 在构架的一开始就有很多事情要考虑。 从高处看,摆在开发者面前有很多问题:要考虑是怎样建立用户接口?在哪里处理业务逻辑? 怎样持久化的数据。 而这三层构架中,每一层都有他们要仔细考虑的。 各个层该使用什么技术? 怎样的设计能松散耦合还能灵活改变? 怎样替换某个层而不影响整体构架?应用程序如何做各种级别的业务处理(比如事务处理)?


 


构架一个Web应用需要弄明白好多问题。 幸运的是,已经有不少开发者已经遇到过这类问题,并且建立了处理这类问题的框架。 一个好框架具备以下几点: 减轻开发者处理复杂的问题的负担(“不重复发明轮子”); 内部有良好的扩展; 并且有一个支持它的强大的用户团体。  好的构架一般有针对性的处理某一类问题,并且能将它做好(Do One Thing well)。 然而,你的程序中有几个层可能需要使用特定的框架,已经完成的UI(用户接口) 并不代表你也可以把你的业务逻辑和持久逻辑偶合到你的UI部分。  举个例子, 你不该在一个Controller(控制器)里面写JDBC代码作为你的业务逻辑, 这不是控制器应该提供的。  一个UI 控制器应该委派给其它给在UI范围之外的轻量级组件。 好的框架应该能指导代码如何分布。 更重要的是,框架能把开发者从编码中解放出来,使他们能专心于应用程序的逻辑(这对客户来说很重要)。


 


这篇文章将讨论怎样结合几种著名的框架来使得你的应用程序做到松弛耦合。


如何建立你的架构,并且怎样让你的各个应用层保持一致。?如何整合框架以便让每个层在以一种松散偶合的方式彼此作用而不用管低层的技术细节?这对我们来说真是一种挑战。  这里讨论一个整合框架的策略( 使用3 种受欢迎的开源框架)  :表示层我们用Struts 业务层我们用Spring;而持久层则用Hibernate 你也可以用其他FrameWork替换只要能得到同样的效果。 见图1 (框架组合示意图)



 


应用程序的分层


大部分的Web应用在职责上至少能被分成4层。 这四层是:presentation(描述),persistence(持久),business(业务)和domain model(域模块)。每个层在处理程序上都应该有一项明确的责任, 而不应该在功能上与其它层混合,并且每个层要与其它层分开的,但要给他们之间放一个通信接口。   我们就从介绍各个层开始,讨论一下这些层应该提供什么,不应该提供什么。


 


表示层(The Presentation Layer)


一般来讲,一个典型的Web应用的的末端应该是表示层。 很多Java发者也理解Struts所提供的。 象业务逻辑之类的被打包到org.apache.struts.Action. 因此,我们很赞成使用Struts这样的框架。


 


下面是Struts所负责的:


     *  管理用户的请求,做出相应的响应。


     *  提供一个Controller ,委派调用业务逻辑和其它上层处理。


     *  处理异常,抛给Struts Action


     *  为显示提供一个模型


     *  UI验证。 


 


以下条款,不该在Struts显示层的编码中经常出现。 它们与显示层无关的。


     * 直接的与数据库通信,例如JDBC调用。


     * 与你应用程序相关联的业务逻辑以及校验。


     * 事物管理。


在表示层引入这些代码,则会带来高偶合和麻烦的维护。


 


 


持久层(The Persistence Layer)


典型的Web应用的另一个末端是持久层。这里通常是程序最容易失控的地方。开发者总是低估构建他们自己的持久框架的挑战性。系统内部的持续层不但需要大量调试时间,而且还经常缺少功能使之变得难以控制,这是持久层的通病。 还好有几个ORM开源框架很好的解决了这类问题。尤其是Hibernate Hibernatejava提供了OR持久化机制和查询服务, 它还给已经熟悉SQLJDBC API Java开发者一个学习桥梁,他们学习起来很方便。 Hibernate的持久对象是基于POJOJava collections。此外,使用Hibernate并不妨碍你正在使用的IDE


 


请看下面的条目,你在持久层编码中需要了解的。


*  查询对象的相关信息的语句。 Hibernate通过一个OO查询语言(HQL)或者正则表达的API来完成查询。  HQL非常类似于SQL– 只是把SQL里的tablecolumnsObject和它的fields代替。 你需要学习一些新的HQL语言; 不管怎样,他们容易理解而文档也做的很好。 HQL是一种对象查询的自然语言,花很小的代价就能学习它。


*  如何存储,更新,删除数据库记录。


*  Hibernate这类的高级ORM框架支持大部分主流数据库,并且他们支持 Parent/child关系,事物处理,继承和多态。


 


业务层(The Business Layer


     一个典型Web应用的中间部分是业务层或者服务层。 从编码的视角来看,这层是最容易被忽视的一层。 而我们却往往在UI层或持久层周围看到这些业务处理的代码,这其实是不正确的,因为它导致了程序代码的紧密偶合,这样一来,随着时间推移这些代码很难维护。幸好,针对这一问题有好几种Frameworks存在。 最受欢迎的两个框架是SpringPicoContainer 这些为也被称为microcontainers,他们能让你很好的把对象搭配起来。 这两个框架都着手于‘依赖注射’(dependency injection)(还有我们知道的‘控制反转’Inversion of Control=IoC)这样的简单概念。 这篇文章将关注于Spring的注射(译注:通过一个给定参数的Setter方法来构造Bean,有所不同于Factory, Spring还提供了Setter Injection(type2)Constructor Injection(type3)等方式供我们选择。  Spring把程序中所涉及到包含业务逻辑和DaoObjects——例如transaction management handler(事物管理控制)、Object Factoris(对象工厂)service objects(服务组件)——都通过XML来配置联系起来。


 


后面我们会举个例子来揭示一下Spring 是怎样运用这些概念。 


业务层所负责的如下:


* 处理应用程序的 业务逻辑和业务校验


* 管理事物


* 允许与其它层相互作用的接口


* 管理业务层级别的对象的依赖。


* 在显示层和持久层之间增加了一个灵活的机制,使得他们不直接的联系在一起。


* 通过揭示 从显示层到业务层之间的Context来得到business services


* 管理程序的执行(从业务层到持久层)。


 


 


域模块层(The Domain Model Layer


既然我们致力于的是一个不是很复杂的Web的应用, 我们需要一个对象集合,让它在不同层之间移动的。  域模块层由实际需求中的业务对象组成 比如, OrderLineItem , Product等等。 开发者在这层 不用管那些DTOs,仅关注domain object即可。 例如,Hibernate允许你将数据库中的信息存放入对象(domain objects),这样你可以在连接断开的情况下把这些数据显示到UI层。 而那些对象也可以返回给持续层,从而在数据库里更新。 而且,你不必把对象转化成DTOs(这可能似的它在不同层之间的在传输过程中丢失),这个模型使得Java开发者能很自然运用OO,而不需要附加的编码。


一个简单例子


 


既然我们已经从全局上理解这些组件。 现在就让我们开始实践吧。 我们还是用 StrutsSpring Hibernate。这三个框架已经被描述够多了,这里就不重复介绍了。 这篇文章举例指导你如何使用这三个框架整合开发,向你揭示 一个请求是如何贯穿于各个层的。(从用户的加入一个Order到数据库,显示;进而更新、删除)。


 


从这里可以下载到程序程序原代码(download


 


既然每个层是互相作用的,我们就先来创建domain objects首先,我们要在这些Object中要确定那些是需要持久化的,哪些是提供给business logic,那些是显示接口的设计。  下一步,我们将配置我们的持久层并且定义好HibernateOR mappings。然后定义好Business Objects。有了这些组成部分之后,我们将 使用Spring把这些连接起来。 最后,我们提供给Spring一个持久层,从这个持久层里我们可以知道它是如何与业务逻辑层(business service layer)通信的,以及它是怎样处理其他层抛出的异常的。。


 


域对象层(Domain Object Layer


 


这层是编码的着手点,我们的编码就从这层开始。 例子中Order OrderItem 是一个OneToMany的关系。 下面就是Domain Object Layer的两个对象:


 


·     com.meagle.bo.Order.java: 包含了一个Order的概要信息


·     com.meagle.bo.OrderLineItem.java: 包含了Order的详细信息


好好考虑怎你的package命名,这反应出了你是怎样分层的。 例如 domain objects在程序中可能打包在com.meagle.bo内。  更详细一点将打包在com. meagle.bo的子目录下面。business logic应该从com.meagle.serice开始打包,而DAO 对象应该位于com.meagle.service.dao.hibernate。反应FormsActions 持久对象(presentation classes) 应该分别放在 com.meagle.actioncom.meagle.forms包。 准确的给包命名使得你的classes很好分割并且易于维护,并且在你添加新的classes时,能使得程序结构上保持上下一致。


持久层的配置(Persistence Layer Configuration


建立Hibernate的持久层 需要好几个步骤。 第一步让我们把BO持久化。 既然Hibernate是通过POJO工作的, 因此Order OrderLineItem对象需要给所有的fileds 加上getter,setter方法。 Hibernate通过XML文件来映射(OR)对象,以下两个xml文件分别映射了Order OrderItem对象。(这里有个叫XDoclet工具可以自动生成你的XML影射文件)



  • Order.hbm.xml
  • OrderLineItem.hbm.xml

你可以在WebContent/WEB-INF/classes/com/meagle/bo目录下找到这些xml文件。Hibernate SessionFactory 是用来告诉程序 应该与哪个数据库通信,该使用哪个连接池或使用了DataSource 应该加载哪些持久对象。而Session接口是用来完成SelectingSavingDeleteUpdating这些操作。

2004年12月04日

http://www.dcl.hpi.uni-potsdam.de/folien/Net-Days/AOP-with-NET_files/frame.htm

2004年12月03日

IKVM的定位是基于.NET CLRJava虚拟机。
?
这是一个开源的项目,主页地址:IKVM

基于.NETJava虚拟机意味着什么?意味着我们可以让Java程序跑在.NET上,可以通过虚拟机这个中介让Java程序和.NET应用程序一起协同工作


更难能可贵的是,IKVM同时支持微软的.NET Framework Mono


IKVM的技术特性包括:


1、 可以静态和动态(运行时)把Java的字节代码转换为.NET IL形式;


2 、包括了一个Java的标准库,这个标准库已经静态编译成了.NET IL的形式;


3 、提供力JNI 接口,可以让Java程序通过.NET 或者 Mono 访问本地资源。


4 、提供了一个供Java调用.NET 类的Stub


?


IKVM现在可以提供对JDK 1.4很好的支持。


IKVM目前不成熟的地方在以下几个方面:


1 AWTSwing没有提供支持,在IKVM的开发计划中,这项优先级别不高。


2 Java的安全模型没有实现。



 一般来说我们可以在一下
3种情况下使用IKVM


1 直接在IKVM下运行Java程序,比如:


        ikvm -jar myapp.jar


2 IKVM来把Java类库转换成.NET类库,比如:


ikvmc -target:library mylib.jar ?可以生成: mylib.dll


     3 使用ikvmstub 来用Java开发.NET程序。


?


一些和IKVM项目相关的资料:



http://weblog.ikvm.net/