|
用户名:HairRoot 笔名:HairRoot 地区: 行业:其他 |
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
一直想找个好一点的方法来记录一些东西, 比如看电子文档中认为比较重要的地方, 以前用过电子工具, 如电子日记本, 后来竟然有一次丢失数据, 损失惨重, 以后不敢再用了。 我想博客是一个好的解决我的问题的地方, 在我成长的路上, 我会好好爱护它 ^_^
如何进行package的设计
我们设计在小的应用程序的时候, 设计出来自己的类并不需要太多关注分包的问题, 然而当设计大的应用程序的时候就必须要分包了。
然而类与类之间有的时候有依赖关系, 这种依赖关系甚至可能是跨越包延伸到其他的包, 因此包与包也是有依赖关系的。怎么样对包进行管理?
如下的问题需要进行考虑:
1)在向包中分配类的时候应该遵照什么原则?
2)应该使用什么设计原则来管理包之间的关系?
3)包的设计原则应该先与类(自上而下)?还是类的设计先于包?(自下而上)
4)如何实际表现出包?在C++中如何表现?在java中如何表现?
5)包创建好了之后, 将它们用于何种目的?
============================================================
自下而上的包设计的几个原则:
包的内聚性原则:
{
1)共同重用原则。
对于使用者来说, 当依赖于一个包的时候, 应该是依赖于一个包中全部的类, 而不是仅仅某一个类。换句话说, 包中的类应该是不可分开的。否则的话,包将直接影响使用者的不必要的重新发布,编译。(这个原则能够避免报分得过于粗。)
2)共同封闭原则
包中的类对于同一类性质的变化应该是共同封闭的。一个变化如果如果对一个包产生影响,那么他对包中的类都产生影响,而对于其他的包不产生影响。(这个原则可以避免包分得过细!)
}
包的依赖性原则:
{
1)无环依赖原则。在包的依赖图中不允许存在环。
“有这种工作经历么? 工作一整天终于完成某个功能后回家,第二天清早去上班却发现那项功能却不能工作,原因是什么呢? 因为有人走得比你更晚,并且更改了你所依赖的那些东西。”---晨后综合症。
2)稳定依赖原则。
朝着稳定的方向进行依赖。
稳定性?
如果改变一个事物需要很大的力气,也就是难以改变, 那么它就是稳定的。
使一个类稳定的方法是让很多的类依赖于它,这样它就具有拒绝变化的n个理由,就是稳定的。相反,如果一个类没有任何类依赖于自己,反而依赖于其他的类,那么就是不稳定的。
3)稳定抽象原则。
抽象类通常是很灵活的, 能够适应OCP的需求。 包的抽象程度应该跟它的稳定性一致。
即:越是稳定的包,其中的类应该是最抽象的。
}
摘自《敏捷软件开发》。
DIP 依赖倒置原则
Robert C.Martin说:
我们更希望重用的是高层的策略设置模块。我们已经非常擅长通过子程序库的形式来重用低层模块。如果高层模块依赖于低层模块,那么在不同的上下文中重用高层模块将变得非常困难。然而,如果高层模块独立于低层模块, 那么高层将变得非常容易重用。该原则是做“Framework”设计的核心。
解决依赖这个问题的本质是使用抽象。我们让高层模块依赖于抽象(抽象类或者接口),而让低层模块来实现抽象。从原理上, 这个原则就是:
1) 任何变量都不能拥有一个具体类的指针或者引用。
2)任何类都不应该从具体类派生
3)任何方法都不应该覆写基类中已经实现的方法。
这个原则对于那些虽然具体但是却稳定的类来说似乎并不是很合适, 如果一个类不太会改变, 而且也不太可能创建其他的派生类,那么依赖它似乎并没有太大的危害。比如java的String类。
什么是成功
每个人都是很复杂的动物。人有自己的欲望,有自己想追求的东西。而这些东西往往不只是一个。
钱对于一般人来说都是自己想追求的东西, 但是往往绝非是唯一的追求。
有一种人除了喜欢金钱之外, 还喜欢权力。
而第二种人喜欢钱之外还喜欢一种叫做幸福的东西。
可惜的是很多人只是注意到人的同性的一面, 而没有看到个性差异的一方面。
当我们看到某个人在公司步步高升,权力越来越大,待遇越来越好的时候, 看到自己的待遇不如别人, 心理就有点不平衡了,无形中就会思考, 他能够做到这样自然有他比我强的地方,可能是他很善于相处跟领导的关系,很有心眼, 我自己在这方面应该好好的跟他学习学习啊,也不能做得太差了。
跟别人的对比是人潜意识里边的东西,时常就会在脑中产生。潜意识的东西往往没有经过太多的思考时间, 它是否就真的那么可靠。试想一下如果给第二种金钱的同时, 还给与他很大的权力, 他会不会幸福?金钱是他想要的, 但是权力呢?获得权力的同时也就意味着更大的责任和压力, 在更大的压力的面前,他会感到他所期望的幸福吗?我敢说绝对不会。
当一个人觉得迷茫的时候, 应该好好想想他说期望的生活是怎么样的, 好好了解自己。如果一个人经常迷茫, 只能说明他要不就没有思考过这个问题, 要不就是心里太浮躁了。
想起了周润发的一句广告词,“什么是成功?。。。”。
成功的含义是实现了自身的价值, 而不是实现了别人的价值!
xml schema
xml schema的基础知识最好的教材就是w3c的
http://www.w3.org/TR/xmlschema-0
这个网页上讲到的东西如果掌握了,基本上都能够对实际中所有遇到的xmlInstance进行schema约束。
下面说一些理解schema过程中遇到的一些稍微费解的地方:
1。targetNamespace
schema规范中定义了一些基本的数据类型,比如我们经常用到simpleType的string, float, date,,,这些基本类型, 还有complexType的anyType。这些type的namespace都是标准的http://www.w3.org/2001/XMLSchema, 如果想要使用shema的基本类型, 就必须加上这个Namespace, 实际中往往要定义自己的simpleType和complexType, 而为了把自定义的type跟schema类型分开, 使用targetNamespace即可指定自定义的type的namespace, 而targetnamespace的值往往是异于http://www.w3.org/2001/XMLSchema的。
在设计schema的时候,通常都需要引入http://www.w3.org/2001/XMLSchema即shema的标准namespace, 如果需要使用其它已经存在的schema的一些类型, 那么就同样需要引入对应的namespace, 也就是被引入的schema中指定的targetNamespace的值。
2。elementFormDefault
schema中可以定义全局的element, 比如:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schema" xmlns:ns="http://schema">
<xs:element name="a" type="ns:c"/>
<xs:complexType name="c">
<xs:sequence>
<xs:element name="d" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
<xs:schema>
a就是一个全局的elemnt, 还有一些element是定义在complexType里边的,比如
c里边的element d就是一个局部的element.
全局element和局部的element一个很大的不同是:
全局变量a具有namespace "http://schema",而局部变量d的namespace为"".
所以下面的xml instance是错误的,
<a xmlns="http://schema">
<d>true</d>
</a>
而
<a xmlns="http://schema">
<d xmlns="">true</d>
</a>
就是对的。
这一定让很多刚开始学习schema的人感到不知所措。
事实上,实际中我们用得更多的是前面一个xml instance,但是它就是不能通过上面的shema的校验。既然习惯用前一个xml instance, 那么就当然需要修改schema的定义使得校验能够通过。
怎么修改:
两个方法:
1)
<xs:complexType name="c">
<xs:sequence>
<xs:element name="d" type="xs:boolean" form="qualified"/>
</xs:sequence>
</xs:complexType>
在d上加一个form="qualified", 这样d也具有namespace "http://schema"
2)在schema的属性上加上elementFormDefault="qualified"
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schema" xmlns:ns="http://schema" elementFormDefault="qualified">
3。schema import
现实中会遇到一些比较复杂xml, 比如
<?xml version="1.0" encoding="UTF-8"?>
<a xmlns="http://schema1" xmlns:edi="http://ccc.cc">
<b edi:attr1="123" attr2="1.112">qwe</b>
<edi:a>
<d>false</d>
</edi:a>
</a>
这个xml复杂的地方在于element a的namespace跟它下面的子元素的<edi:a>的namespace不同,element b的attribute中又含有edi:attr1。那么这个xml怎么来用schema来对它进行约束?
答案是,需要用到schema import的功能。
也即需要编写两个schema文件,其中一个用来对element a,b来进行约束,targetNamespace为"http://schema1", 另外一个schema对<edi:a>, <edi:attr1>来进行约束, targetNamespace为"http://ccc.cc", 前面的schema由于需要用到后面的schema, 所以需要将后者import。
schema1.xsd如下:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://schema1" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:edi="http://ccc.cc" xmlns:schema1="http://schema1">
<xs:import namespace="http://ccc.cc" schemaLocation="edi.xsd"/>
<xs:element name="a">
<xs:complexType>
<xs:sequence>
<xs:element name="b">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="edi:attr1"/>
<xs:attribute name="attr2" type="xs:float"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element ref="edi:a"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="d" type="xs:boolean"/>
</xs:schema>
edi.xsd如下:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ccc.cc" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://schema1">
<xs:import namespace="http://schema1" schemaLocation="schema1.xsd"/>
<xs:element name="a">
<xs:complexType>
<xs:sequence>
<xs:element ref="ns1:d"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:attribute name="attr1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:schema>
xml namespace
关于xml namespace的详细规范,参考:http://www.w3.org/TR/REC-xml-names/
A default namespace is considered to apply to the element where it is declared (if that element has no namespace prefix), and to all elements with no prefix within the content of that element. If the URI reference in a default namespace declaration is empty, then unprefixed elements in the scope of the declaration are not considered to be in any namespace. Note that default namespaces do not apply directly to attributes.
1. 什么是defaultNamespace?
就是xmlns属性的值。defaultNamespace的作用就是它下面的没有prefix的element的Namespace就是defaultNamespace. 示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--defaultNamespace为http://schema, a的namespace为http://schema1 -->
<a xmlns="http://schema1" xmlns:edi="http://ccc.cc">
<!--b由于没有前缀,所以会自动采用defaultNamespace, 即http://schema1-->
<b edi:attr1="123" attr2="1.112">qwe</b>
<edi:a xmlns=http://xxx.xxx>
<!--d的namespace不再是http://schema1, 而是http://xxx.xxx, 因为d的父节点也定义了一个defaultNamespace: http://xxx.xxx-->
<d>false</d>
</edi:a>
<edi:a>
<!--注意,虽然e的父节点Namespace为http://ccc.ccc, 但是e的namespace并不是http://ccc.cc, 而是http://schema1-->
<e>false</e>
</edi:a>
</a>
2.如果一个element定义了xmlns="xxx"属性, 那么直接具有xxx的Namespace, 除非这个element自己包含一个前缀。
...
<!-- a 具有xxx 命名空间 -->
<!-- 属性d不具有xxx命名空间 -->
<a xmlns="xxx" d="ddd">
<!--b也具有xxx命名空间 -->
<b>
...
</b>
<!--c不具有xxx的命名空间-->
<edi:c>
...
<edi:c>
</a>
...
<!--a不具有xxx命名空间, 因为它使用着edi的命名空间-->
...
<edi:a xmlns="xxx">
<b>
...
</b>
</edi:a>
...
5.3 Uniqueness of Attributes
In XML documents conforming to this specification, no tag may contain two attributes which:
have identical names, or
have qualified names with the same local part and with prefixes which have been bound to namespace names that are identical.
For example, each of the bad start-tags is illegal in the following:
<!-- http://www.w3.org is bound to n1 and n2 -->
<!--没有prefix的attribute不具有任何命名空间-->
<x xmlns:n1="http://www.w3.org"
xmlns:n2="http://www.w3.org" >
<bad a="1" a="2" />
<!--n1:a和n2:a的命名空间和属性名均相同,不允许出现在同一个element中-->
<bad n1:a="1" n2:a="2" />
</x>
However, each of the following is legal, the second because the default namespace does not apply to attribute names:
<!-- http://www.w3.org is bound to n1 and is the default -->
<x xmlns:n1="http://www.w3.org"
xmlns="http://www.w3.org" >
<good a="1" b="2" />
<!--属性a的命名空间为空,而n1:a的命名空间为http://www.w3.org-->
<good a="1" n1:a="2" />
</x>
web service best practice
一个项目中可能包含多个web service, 如果每个web service都用一个单独wsdl来描述, 这样的话, 在客户端每个wsdl必然会对应一个Service Endpoint Interface。
服务端多个wsdl的维护成本必然很高, 不如把多个wsdl合成一个wsdl。
客户端,每次对wsdl的修改, 必然导致客户端对某个service 接口的修改, 不如把客户端分散的service inteface合成一个service interface, 合成的service可能包含多个service方法,但是类的数量却很少, 只有一个。
如果联想到设计模式中facade模式, 还有ejb中session facade模式, 不难发现,上述行为其实就是利用了facade模式。
关于axis1.2中对象的序列化和发序列化
我从开始使用wtp来开发web service开始, 就在思考一个问题:
那些java的对象是可以序列化为xml的, 并且可以从xml反序列化为java对象的?
那些对象与xml之间不能够序列化和反序列化?
在开发的时候应该注意哪些问题?
根据我的理解, 有如下几种对象:
1)axis1.2内在支持的几种对象类型。
这几种内在支持的对象包括:
java基本类型 : int, float,,,,
基本类型包装类 : Integer, Float, Long...
还有String, Date, Calendar, BigDecimal, BigInteger, List, Map.
凡是这些内在支持的对象, 不管他们作为某个Service的input 还是 output, 我们在服务端的axis1.2的WEB-INF/server-config.wsdd的该Service的定义中都不需要加入<beanMapping>或者是<typeMapping>的声明。
2)简单的javabean对象类型。
对于简单的javabean对象, 比如对象中所有的field都是上面提到的基本类型。 axis1.2也提供了很好的支持。
比如:
public class JavaBeanInputService {
public void testJavaBeanInput(MyBean bean) {
......
}
}
由于MyBean是一个自定义的JavaBean对象, 所以在server-config.wsdd中就必须加上<beanMapping ...../>的声明, 让axis知道怎么把request中xml数据deserialize为MyBean对象, 又如何把MyBean对象serialize为xml数据作为response.用wtp自动为JavaInputService生成的wsdl中, MyBean是作为一个complexType在wsdl中定义的。
3)复杂一点的JavaBean对象。
比如JavaBean对象中的一些field又是自定义的JavaBean, 这种情况下, wsdl中生成的complextype会有多个, 而在wsdd定义的<beanMapping .../>也会有多个, axis1.2支持起来都是易如反掌。
4)普通的非javabean对象。
对于一些不是javaBean的对象, wtp也会替你生成对应的wsdl的ComplexType, 依据的是对象的getter方法。但是显然这是不够的。 比如说有些对象的数据结构比较复杂, 像java.util.HashMap(虽然这个已经被axis内在支持了。)这些对象如果想要把自己的状态进行serialization和deserialization, 就得自己编写serializer和deserializer, 而且还必须保证wsdl中的该complexType的描述是正确的。
5)java中的List, Map问题。
试想一下如果一个service的样子是这样子的。
public class ListService{
public List listTest(List list) {
for(Iterator iter = list.iterator(); iter.hasNext(); ) {
(MyBean)list.next();//进行强转。
}
}
}
用wtp为这个service生成的wsdl中把list映射为一个type为xsd:anyType的maxOccurs="unbound"的complexType。这样的话客户端生成的Stub中的接口中类似于:
public interface ListService{
public Object[] listTest(Object[] list) ;
}
如果Client端用户传递的入口参数是String[],那么在服务端执行的必然会发生转型错误。
因此,在webservice中把List, Map作为service的input, output的做法都是不可行的。至少在jdk1.4的版本中是这样的。
一个更好的方法就是:
6)java中的数组。
上例中的ListService如果改造为下面这样,基本上就没有上面提到的问题了。
public class ListService{
public MyBean[] listTest(MyBean[] list) {
...
}
}
这样在wsdl中, MyBean被映射为一种ComplexType,MyBean[]为映射为ComplexType为映射为可以重复出现的MyBean类型。在客户端的Stub的接口跟这个也是类似的。从而也成功地避免了List, Map中型别问题。
要注意的是,在server-config.wsdd中需要配置<arrayMapping.../>
似乎List, Map的问题用数组就可以解决了。事实上就是如此。但是还得注意的是:
javabean里边也不能含有List. 如果MyBean跟其它某个对象是1:n的关系,那么也只能写成数组的形式,而不能是List的形式。
7)特殊对象java.lang.Object
如果一个service写成了下面的形式:
public class ObjectService{
public Object objInvoke(Object obj) {
...
}
}
想把它发布为web service, 那么几乎是不太可能的。遇到obj类型,wsdl里边只能定义为xsd:anyType类型,而这种类型如果给客户端返回一个比如MyBean类型,那么必然会导致xml的serialization的失败。结论就是:
web service中如果input 或者是output是java.lang.Object类型,那么将会导致严重问题。
上面的几种对象类型基本上能够涵盖将java class发布为web service时需要考虑的对象类型。可以看到开发web service的时候,并不是所有的java都能够轻而易举地发布为web service, 一些复杂的类的对象类型,还有一些特殊的对象类型都是要考虑的。最后一个问题是:子类是否也很容易的得到序列化和反序列化?
答案是肯定的。如下的Service:
public class PolymorphicService{
public MyBean objInvoke(MyBean obj) {
...
}
}
客户端的Stub如下:
public class PolymorphicServiceStub{
public MyBean objInvoke(MyBean obj) {
...
}
}
如果在客户端调用stub时传入的不是MyBean类的对象,而是它的子类的一个对象,那么也可以被序列化而传到服务端。同样,如果服务端返回的对象是MyBean类的字类的一个对象,也可以成功的被序列化到客户端。
在eclipse的环境下使用wtp开发web service
很多人知道MyEclipse是一款很不错的开发j2ee的eclipse的插件, 但是MyEclipse到现在的版本中都没有提供对web service良好的支持。于是找来找去, 还好找到了另外一个对web service支持的比较好的插件, 那就是eclipse的官方j2ee插件wtp.
开发环境:eclipse3.1 + wtp0.7 + tomcat5.5
一切准备就绪, 下面就可以开始第一个自己的web service,心中充满了期待。 :)
step 1:新建web project
将eclipse的perspective切换到wtp提供的j2ee的视图下。新建一个Dynamic web project取名为webservicedemo, 可以看到在webservicedemo project下面有三个文件目录。其中JavaSource是java源代码的目录, WebContent是web资源的目录, 包括jsp, web.xml等等资源。
step2: 新建一个简单的java class。
在JavaSource下新建一个自己的java class, 比如com.hairroot.test.logic. TransferMoney
public boolean transferMoney(float amount, String fromAccoutId, String
toAccountId) {
System.out.println("transfering "+amount+" money from Account "
+ fromAccoutId+" to "+toAccountId);
return true;
}
step3: 使用wtp发布java class.
右键TransferMoney class, 在右键菜单中可以看到有wtp提供的一个Web Services的支持。点击Create Web Service.这样按照wtp提供的wizard, 一步一步走下去就可以很顺利地将自己的java class发布成为web service.
事情就是这么简单, 当发布成功并且我可以在wtp自己为我建的webservicedemoClient的环境下测试自己的web service的时候, 心中还是万分激动的。:)
事实上, wtp0.7使用的axis1.2的版本来对web service来进行支持。 没有axis学习经验的人可能完全不知道自己的web service是怎么被发布出来的, 不过这都没有关系, 重要的是使用wtp的确能够使开发-发布web service变得很简单。
那么对于一个项目而言, 开发访问web service的模式是怎样的?
Server Side:
对于很多旧系统而言, 对外提供web service服务可能是到后期才想到的问题。并非所有的类型的java class都适合发布为web service, 对于一个设计的比较好的j2ee系统来说,只有bussiness logic这一层比较适合发布为web service, 可能很多系统在框架下开发不是很重视j2ee的层次,也不去分bussiness logic, 这样的系统如果要提供某些web service, 那就要重新编写代码了。然而一般来说, 将bussiness logic发布为web service是一个比较好的选择。上面的TransferMoney这个类可能看起来很简单, 都是一些primitive type的参数, 对于复杂的javabean或者一些非javabean对象, axis同样能够良好的支持。
Client Side:
在Server Side使用wtp发布了一个Web Service, wtp还会自动为该java class生成对应的wsdl文件,也不需要程序员自己去写, 还是挺方便的。
对Client端开发来说, 有两种方式可以访问某个Web Service:
1.下载wsdl, 根据wsdl里边提供的endpointurl, 还有request, response的格式,使用axis的api来访问某个web service。但是这个方法显然是个笨办法。更好的方法是下面的方法
2. 通过axis的工具来利用wsdl来自动生成客户端的Endpoint Interface, Proxy, Service, ServiceLocator, Stubd等类。让客户端的调用跟直接调用本地的java对象一样的简单, 而不用去管什么namespace, qname的东西。
事实上wtp在发布web service的时候, 有一个选项就是是否生成客户端的代码。所以甚至在发布的时候就可以直接将Stub代码发给Client来使用。