使用策略模式,注意以下三个设计原则:
浮点数值不适用于禁止出现舍入误差的金融计算中。例如,命令System.out.println(2.0-1.1)将打印出0.89999999999999,而不是人们希望的0.9。
1,$ java -verbose:gc <クラス名>
private static final String ZERO_PADDING_PROP_FILE = "JSMI_QueryZeroPadding";
如果希望将一个数组的值拷贝到另外一个数组中去,也就是所谓的深拷贝,JAVA的实现方式可以使用System类的arraycopy方法。调用这个方法的语法格式为:
System.arraycopy(from, fromIndex, to, toIndex, count) ;
数组to必须有足够的空间来存放拷贝的元素。
数组排序:可以使用Arrays类中的sort方法:
int[] a = new int[1000] ;
...
Arrays.sort(a) ;
这个方法是用了优化的快速排序算法。
判断两个数组是否相等(两个数组的长度相同,并且下标相同的元素都对应相等,则返回true)
Arrays.equals(type[] a , type[] b) ;
另外,数组还可以使用二分搜索法查找值v。如果找到,可以返回数组下标;否则,返回一个负值r; -r~1为保持有序v应插入的位置。
java.util.Arrays
static int binarySearch(type[] a, type v) ;
还有一个方法,是将一个数组所有的元素设置为v。
static void fill (type[] a, type v) ;
PMD 包含 16 个规则集,涵盖了 Java 的各种常见问题,其中一些规则要比其他规则更有争议:
基本(rulesets/basic.xml)—— 规则的一个基本合集,可能大多数开发人员都不认同它: catch 块不该为空,无论何时重写 equals(),都要重写 hashCode(),等等。
命名(rulesets/naming.xml)—— 对标准 Java 命令规范的测试:变量名称不应太短;方法名称不应过长;类名称应当以小写字母开头;方法和字段名应当以小写字母开头,等等。
未使用的代码(rulesets/unusedcode.xml)—— 查找从未使用的私有字段和本地变量、执行不到的语句、从未调用的私有方法,等等。
设计(rulesets/design.xml)—— 检查各种设计良好的原则,例如: switch 语句应当有 default 块,应当避免深度嵌套的 if 块,不应当给参数重新赋值,不应该对 double 值进行相等比较。
导入语句(rulesets/imports.xml)—— 检查 import 语句的问题,比如同一个类被导入两次或者被导入 java.lang 的类中。
JUnit 测试(rulesets/junit.xml)—— 查找测试用例和测试方法的特定问题,例如方法名称的正确拼写,以及 suite() 方法是不是 static 和 public。
字符串(rulesets/string.xml)—— 找出处理字符串时遇到的常见问题,例如重复的字符串标量,调用 String 构造函数,对 String 变量调用 toString() 方法。
括号(rulesets/braces.xml)—— 检查 for、 if、 while 和 else 语句是否使用了括号。
代码尺寸(rulesets/codesize.xml)—— 测试过长的方法、有太多方法的类以及重构方面的类似问题。
Javabean(rulesets/javabeans.xml)—— 查看 JavaBean 组件是否违反 JavaBean 编码规范,比如没有序列化的 bean 类。
终结函数(finalizer)—— 因为在Java语言中,finalize()方法不是那么普遍(我上次编写这个代码也经是好多年前的事了),所以它们的使用规则虽然很详细,但是人们对它们相对不是很熟悉。这类检查查找 finalize() 方法的各种问题,例如空的终结函数,调用其他方法的 finalize() 方法,对 finalize() 的显式调用,等等。
克隆(rulesets/clone.xml)—— 用于 clone() 方法的新规则。凡是重写 clone() 方法的类都必须实现Cloneable,clone()方法应该调用super.clone(),而 clone()方法应该声明抛出 CloneNotSupportedException 异常,即使实际上没有抛出异常,也要如此。
耦合(rulesets/coupling.xml)—— 查找类之间过度耦合的迹象,比如导入内容太多;在超类型或接口就已经够用的时候使用子类的类型;类中的字段、变量和返回类型过多等。
严格的异常(rulesets/strictexception.xml)—— 针对异常的测试:不应该声明该方法而抛出 java.lang.Exception异常,不应当将异常用于流控制,不应该捕获 Throwable,等等。
有争议的(rulesets/controversial.xml)—— PMD 的有些规则是有能力的Java程序员可以接受的。但还是有一些争议。这个规则集包含一些更有问题的检验,其中包括把null赋值给变量、方法中有多个返回点,以及从 sun 包导入等。
日志(rulesets/logging-java.xml)—— 查找 java.util.logging.Logger 的不当使用,包括非终状态(nonfinal)、非静态的记录器,以及在一个类中有多个记录器。
/**Action类
Struts1要求Action类继承一个抽象类,Struts1使用抽象类编程而不是接口
Struts2可以实现Action接口也可以不实现,Struts2提供一个ActionSuport基类去实现常用接口。Action接口不是必须的,任何有execute的标识的POJO都有可能成为struts2的Action的对象
/**线程模式
Struts1的Action是单例模式并且是线程安全的,仅有Action一个实例来处理所有请求。单策略模式限制了Struts1 Action能做的事。比且在开发时要注意Action资源必须是线程安全比且是同步的。
Struts2的Action对象为每个请求产生一个实例,因此没有线程安全问题。
/**Servlet依赖
Struts1 Action依赖与Servlet API,因为当一个Action被调用时HttpServletRequest和HttpServletResponse被传递给execute方法。
Struts2 Action不依赖与容器,允许Action脱离容器被单独测试。如果需要,struts2依然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServletRequest和HttpServletResponse的必要性。
/**可测性:
测试Struts1 Action的一个主要问题是execute方法暴漏了Servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)
struts2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。
/**捕获输入:
struts1使用ActionForm对象捕获输入。所有ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经常创建多余的基类不或输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存在的JavaBean(仍然会导致有沉余的JavaBean)。
Struts2直接使用Acrtion属性作为输入属性,消除了对第二个输入对象的请求。输入属性可能是有自己(子)属性的Rich对象类型。Action属性能过通过web页面上的Taglibs访问。struts2也支持ActionForm模式。Richard对象类型,包括业务对象,能够用作输入/输出对象。这种ModelDriven特性简化了Taglib对POJO输入对象的引用。
/**表达式语言
Struts1整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很脆弱。
Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--“object graph Notation Language”(OGNL)
/**绑定值到页面(view)
Struts1使用标准JSP机制吧对象绑定到页面中来访问。
struts2使用ValueStack技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重同页面(view)
/**类型转换
Struts1 ActionForm属性通常都是String类型。struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
struts2 使用OGNL进行类型转换,提供基本和常用对象的转换器。
/**效验
Struts1支持在ActionForm的validate方法中手动效验,或者通过Commons Validator的扩展来效验。同一个类可以有不同的效验内容,但不能效验子对象。
Struts2支持通过validate方法和XWork效验框架进行效验。XWork效验框架使用为属性类类型定义的效验和内容效验,来支持Chain效验子属性。
/**Action执行的控制
Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不能的生命周期。堆栈能够根据需要和不同的Action一起使用。
定义:
提供一个创建一系列相关或者相互依赖对象的接口,而无需制定它们具体的类。抽象工厂(Abstract Factory)模式又成为Kit模式,属于对象创建型模式。
抽象工厂模式与工厂方法模式最大的区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则针对的是多个产品等级结构。正因为如此,在抽象工厂模式中经常会用到产品族(Product Family)这一概念,它指的是位于不同的产品等级结构中,并且功能相互关联的产品系列。
应用情景:
1,系统需要屏蔽有关对象如何创建,如何组织和如何表示。
2,系统需要由关联的对个对象来构成。
3,有关联的多个对象需要一起应用并且它们的约束是强迫的(不可分离)。
4,你想提供一组对象而不显示它们的实现过程,只显示它们的接口。
跟我学习设计模式之第二篇:工厂方法模式开篇了。下面我们来看看其定义:
工厂方法模式又称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,属于类的创建型模式。在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实例化(创建)哪一个类。
下面以一个JAVA实例应用来看了解一下具体需求:
现实中不同品牌的手机应由不同的工厂制造,下面应用工厂模式来解决这个问题。
手机接口:
public interface Mobile {
public void call();
}
手机工厂接口:
public interface MobileFactory {
public Mobile productMobile();
}
摩托罗拉手机实现手机接口:
public class Motorola implements Mobile {
public void call() {
System.out.println("MOTOROLA モベイル");
}
}
诺基亚手机实现手机接口:
public class Nokia implements Mobile {
public void call() {
System.out.println("Nokia モベイル");
}
}
摩托罗拉工厂实现了生产手机的方法,返回摩托罗拉手机:
public class MotorolaFactory implements MobileFactory {
@Override
public Mobile productMobile() {
// TODO 自動生成されたメソッド・スタブ
System.out.print("Motorola工場で製造した");
return new Motorola();
}
}
诺基亚工厂实现了生产手机的方法,返回诺基亚手机:
public class NokiaFactory implements MobileFactory {
@Override
public Mobile productMobile() {
// TODO 自動生成されたメソッド・スタブ
System.out.print("Nokia工場で製造した");
return new Nokia();
}
}
客户程序:
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自動生成されたメソッド・スタブ
MobileFactory mf;
Mobile m;
mf = new MotorolaFactory();
m = mf.productMobile();
m.call();
mf = new NokiaFactory();
m = mf.productMobile();
m.call();
}
}
输出:
Motorola工場で製造したMOTOROLA モベイル
Nokia工場で製造したNokia モベイル
下面的情景很适合应用工厂方法模式:
1,类不知道自己要创建哪一个对象;
2,类用它的子类来制定创建哪个对象;
3,客户需要清楚创建了哪一个对象。
对设计模式的理解和应用能力,是区分一个程序员和一个软件工程师的重要特征。从今天开始,我将花费一些时间和精力,用在设计模式的学习和实践上。
今天,首先解决三个问题:
第一,什么是设计模式?
第二,设计模式的作用?
第三,设计模式的分类?
按照顺序依次解决这三个问题,就算完成开篇的内容。
第一,什么是设计模式?
模式的定义:模式是一种问题的解决思路,它已经试用于一个实践环境,并且可以适用于其他环境。
如果觉得抽象,举几个例子马上就可以理解了:用牛耕田,织网捕鱼,打井取水……,所有这些都是前人创造的生活模式,我们大家不自觉的把它们应用在生活的方方面面。
同理,程序设计,也有一些前人的设计思路可供我们直接使用,这就是设计模式。
第二,设计模式的作用?
1,重用设计,比重用代码更有意义,它会自动带来代码重用。
2,为设计提供共同词汇,模式名就是设计词汇,方便交流。
3,开发文档中采用模式词汇让阅读者更容易理解。
4,可以使得重构系统变得容易。确保代码正确,降低设计和实现中出现错误的可能性,为从写其它应用程序提供很好的系统架构。
5,正确使用设计模式,可以节省大量时间。
第三,设计模式的分类?
设计模式种类很多,包括分布式编程模式,用户界面模式,数据模式模式三大类。目前流行的面向对象设计模式,仅1995年“Gang of Four”(四位作者:Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides)描述的就有二十多种,称之为GoF模式,与其相对应的另一种重要的设计模式是通用责任分配软件系列模式(GRASP,General Responsibility Assignment Software Patterns)。
1,GRASP模式的分类:
与其说它是面向对象的设计模式,不如说它是面向对象的设计要求,细分为9种模式。模式之间不是独立的,存在相互平衡的制约关系,所以我们的设计只能努力的满足GRASP的各种模式。
1)Information Expert(信息专家)
2)Creator(创造者)
3)Low coupling(低耦合)
4)High cohesion)(高内聚)
5)Controller(控制器)
6)Polymorphism(多态)
7)Pure Fabrication(纯虚构)
8)Indirection(间接)
9)Protected Variations(受保护变化)
2,GoF模式的分类:
◆根据目的准则分类:
就是说,指定的模式用来完成什么工作。可分三种:
1)Creational(创建型):与对象创建有关
2)Structural(结构型):处理类或对象的组合
3)Behavioral(行为型):描述类或对象如何交互及如何分配职责。
◆根据范围准则分类:
即指定的模式是用于类还是用于对象,分为两种:
1)类模式:编译时就定下来了。
2)对象模式:处理对象间关系,这些关系具有动态性,在运行期间是可以变化的。
范围/目的:Creational(创建型): Creational(创建型): Behavioral(行为型)
类:Simple Factory:Adpter(class):Interpreter
:Factory Method: :Template method
对象:Abstract Facotry:Adapter(object):Chain of Responsibility
:Builder :Bridge :Command
:Prototype :Composite :Iterator
:Singleton :Decorator :Mediator
: :Facade :Memento
: :Flyweight :Observer
: :Proxy :State
: : :Strategy
: : :Visitor
一个用户的请求是通ActionServlet来处理和转发的。那么,ActionServlet如何决定把用户请求转发给哪个Action对象呢?这就需要一些描述用户请求路径和Action衍射关系的配置信息了。在Struts中,这些配置映射信息都存储在特定的XML文件Struts-config.xml中。在该配置文件中,每一个Action的映射信息都通过一个
这些配置信息在系统启动的时候被读入内存,供Struts在运行期间使用。在内存中,每一个
对于采用Struts框架的web应用,在web应用启动时就会加载并初始化ActionServlet,ActionServlet从struts-config.xml文件中读取配置信息,把它们存放到各个配置对象中,例如Action的映射信息存放在ActionMapping对象中。
当ActionServlet接收到一个客户请求时,将执行如下流程:
1.检索和用户请求相匹配的ActionMapping实例,如果不存在,就返回用户请求路径无效信息。
2.如ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中。
3.根据配置信息决定是否需要表单验证。如果需要验证,就调用ActionForm的Validate()方法。
4.如果ActionForm的Validate()方法返回null或返回一个不包含ActionMessage的ActionErrors对象,就表示表单验证成功。
5.ActionServlet根据ActionMapping实例包含的映射信息决定将请求转发给哪个Action。如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法。
6.Action的execute()方法返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指向的JSP组件。
7.ActionForward对象指向的JSP组件生成动态网页,返回给客户。