资源描述
第十六章 JAVA设计模式
学习目标
Ø 理解并掌握Java设计模式的思想。
Ø 能够上机对Java设计模式进行运用。
Ø 能够在项目中的灵活运用Java设计模式。
课前准备
Ø 认识Java设计模式的重要性。
Ø 了解Java设计模式的概念和思想。
16.1 本章简介
就Java语言体系来说,设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。会Java的人越来越多,但是一直徘徊在语言层次的程序员不在少数,真正掌握Java中接口或抽象类的应用不是很多,大家经常以那些技术只适合大型项目为由,避开或忽略它们,实际中,Java的接口或抽象类是真正体现Java思想的核心所在,这些你都将在设计模式里领略到它们变幻无穷的魔力。
设计模式表面上好像也是一种具体的"技术",而且新的设计模式不断在出现,设计模式自有其自己的发展轨道,而这些好像和J2EE .Net等技术也无关!
实际上,设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧,让你能够真正掌握接口或抽象类的应用,从而在原来的Java语言基础上跃进一步,更重要的是,设计模式反复向你强调一个宗旨:要让你的程序尽可能的可重用。
16.2 工厂(Factory)系列
工厂模式定义:提供创建对象的接口。
为何使用?
工厂模式是我们最常用的模式了,工厂模式在Java程序系统可以说是随处可见。
为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 。工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,不必事先知道每次要实例化哪一个类。工厂模式有以下几种形态:
(1)简单工厂 (Simple Factory) 模式,又称静态工厂方法模式(Static Factory Method Pattern)。
(2)工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟造子(Virtual Constructor)模式。
(3)抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或 Toolkit)模式。
16.2.1简单工厂
16.2.1.1简单工厂的定义及理解:
简单工厂模式,或称静态工厂方法模式,是不同的工厂方法模式的一个特殊实现。
在Java语言中,通常的工厂方法模式不能通过设计功能的退化给出静态工厂方法
模式。因为一个方法是不是静态的,对于Java语言来说是一个很大的区别,必须在一
开始的时候就加以考虑。
简单工厂模式就是由一个工厂类可以根据传入的参数决定创建出哪一种产品类的
实例。它涉及到工厂类角色以及具体产品角色等三个角色:
1、 工厂类角色:担任这个角色的是工厂方法模式的核心,含有及应用密切相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java
类实现。
2、 抽象产品角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个Java接口或者Java抽象类实现。
3、 具体产品角色:工厂方法模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现。
16.2.1.2简单工厂的实现及应用:
在真实的系统中,产品可以形成复杂的等级结构,这个时候,简单工厂模式采取的
是以不变应万变的策略,一律使用同一个工厂类。这样做的好处是设计简单,产品类的
等级结构不会反映到工厂中来,从而产品类的等级结构的变化也就不会影响到工厂类。
但是这样做的缺点是:增加新的产品必将导致工厂类的修改。
如果模式所产生的具体产品类彼此之间没有共同的商业逻辑,那么抽象产品角色可
以由一个Java接口扮演;相反,如果这些具体产品类彼此之间确有共同的商业逻辑,
那么这些公有的逻辑就应当移到抽象角色里面,这就意味着抽象角色应当由一个抽象类
扮演。在一个类型的等级结构里面,共同的代码应当尽量向上移动,以达到共享的目的,
如下模拟农场公司示例:
代码:
/**
*定义水果接口规定出所有的水果必须实现的接口,包括任
*何水果类必须具备的方法:种植plant(),生长grow()
*以及收获harvest()。
*@authorxtgj
*/
publicinterface Fruit {
//生长
void grow();
//收获
void harvest();
//种植
void plant();
}
代码:
/**
*Apple是水果类的一种,由于苹果是多年生植物,
*因此多出一个treeAge属性,描述苹果树的树龄。
*@authorxtgj
*/
publicclass Apple implements Fruit {
privateinttreeAge;
publicvoid grow() {
int age = 0 ;
log("Apple is growing...");
this.setTreeAge(age++) ;
}
publicvoid harvest() {
log("Apple has been harvested.");
}
publicvoid plant() {
log("Apple has been planted.");
}
/**
*辅助方法
*/
publicstaticvoid log(String msg)
{
System.out.println(msg);
}
/**
*树龄的取值方法
*/
publicint getTreeAge()
{
returntreeAge;
}
/**
*树龄的赋值方法
*/
publicvoid setTreeAge(int treeAge)
{
this.treeAge = treeAge;
}
/**
*得到果实的方法
*/
public Apple getApple(){
Apple apple = new Apple() ;
apple.plant() ;
apple.grow() ;
apple.harvest() ;
return apple ;
}
}
代码:
/**
*Grape类是水果类的一种,也实现了Fruit接口所声明的所有的
*方法。但由于葡萄分有籽和无籽两种,因此,比通常的水果多出
*一个seedless属性
*@authorxtgj
*/
publicclass Grape implements Fruit {
privatebooleanseedless;
publicvoid grow() {
log("Grape is growing...");
}
publicvoid harvest() {
log("Grape has been harvested.");
}
publicvoid plant() {
log("Grape has been planted.");
}
/**
*辅助方法
*/
publicstaticvoid log(String msg)
{
System.out.println(msg);
}
/**
*有无籽的取值方法
*/
publicboolean getSeedless()
{
returnseedless;
}
/**
*有无籽的赋值方法
*/
publicvoid setSeedless(boolean seedless)
{
this.seedless = seedless;
}
/**
*得到果实的方法
*/
public Grape getGrape(){
Grape grape = new Grape() ;
grape.plant() ;
grape.grow() ;
grape.harvest() ;
return grape ;
}
}
代码:
/**
*Strawberry类也水果类的一种,实现了Fruit接口所声明的所有的
*方法。
*@authorxtgj
*/
publicclass Strawberry implements Fruit {
publicvoid grow() {
log("Strawberry is growing...");
}
publicvoid harvest() {
log("Strawberry has been harvested.");
}
publicvoid plant() {
log("Strawberry has been planted.");
}
/**
*辅助方法
*/
publicstaticvoid log(String msg)
{
System.out.println(msg);
}
/**
*得到果实的方法
*/
public Strawberry getStrawberry(){
Strawberry strawberry = new Strawberry() ;
strawberry.plant() ;
strawberry.grow() ;
strawberry.harvest() ;
returnstrawberry ;
}
}
代码:
/**
*FruitGardener类会根据客户端的要求,创建出不同的水果对象,
*比如苹果(Apple),葡萄(Grape)或草莓(Strawberry)的
*实例。而如果接到不合法的要求,FruitGardener类会抛出
*BadFruitException异常
*@authorxtgj
*/
publicclass FruitGardener {
/**
*静态工厂方法
*/
publicstatic Fruit factory(String which)throws BadFruitException{
if (which.equalsIgnoreCase("apple"))
{
returnnew Apple().getApple();
}
elseif (which.equalsIgnoreCase("strawberry"))
{
returnnew Strawberry().getStrawberry();
}
elseif (which.equalsIgnoreCase("grape"))
{
returnnew Grape().getGrape();
}
else
{
thrownew BadFruitException("Bad fruit request");
}
}
}
代码:
/**
*创建BadFruitException异常类
*@authorxtgj
*/
publicclassBadFruitExceptionextends Exception {
public BadFruitException(String msg)
{
super(msg);
}
}
代码:
/**
*创建客户端,客户端只需调用FruitGardener的静态方法factory()即可
*@authorxtgj
*/
publicclass Client {
publicstaticvoid main(String[] args) {
try
{
FruitGardener.factory("grape");
FruitGardener.factory("apple");
FruitGardener.factory("strawberry");
}
catch(BadFruitException e)
{
System.out.println("未能大丰收!");
}
}
}
16.2.2工厂方法
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
首先,在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。这个核心类变成了一个抽象工厂角色,只负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
这样就使得这种工厂方法模式可以用来允许系统再不修改具体工厂角色的情况下引进新的产品,这一特点无疑使得工厂模式具有超过简单工厂模式的优越性。
16.2.2.1 工厂方法模式涉及到的角色
工厂方法模式的系统涉及到以下的角色:
1、抽象工厂角色:担任这个角色的是工厂方法模式的核心,它是及应用程序无关的。任何在模式中创建对象的工厂类必须实现这个接口。在上面的系统中这个角色由Java接口Creator扮演:在实际的系统中,这个角色也常常使用抽象Java类实现。
2、具体工厂角色:担任这个角色的是实现了抽象工厂接口的具体Java类。具体工厂角色含有及应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。在本系统中给出了两个这样的角色,也就是具体Java类ConcreteCreator1 和ConcreteCreator2。
3、抽象产品角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在本系统中,这个角色由Java接口Product扮演;在实际的系统中,这个角色也常常使用抽象Java类实现。
4、具体产品角色:这个角色实现了抽象产品角色所声明的接口。工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。
代码:
/**
*下面是抽象产品角色的源代码。由于这里考虑的是最为简单的情形,
*所以抽象产品角色仅仅为具体产品角色提供一个共同的类型而已,
*所以是用一个Java标识接口实现的。一个没有声明任何方法的
*接口叫做标识接口。
*@authorxtgj
*/
publicinterface Product {
}
代码:
/**
*创建抽象工厂角色的源代码,这个角色是用一个Java接口实现的,
*它声明了一个工厂方法,要求所有的具体工厂角色都实现这个工厂方法。
*@authorxtgj
*/
publicinterface Creator {
/**
*工厂方法
*/
public Product factory() ;
}
代码:
/**
*下面是具体产品角色CocnreteProduct1的源代码。它是
*此系统向客户端提供的产品,在通常情况下,这个类会有复
*杂的商业逻辑。
*@authorxtgj
*/
publicclass ConcreteProduct1 implements Product {
public ConcreteProduct1(){
System.out.println("do something in ConcreteProduct1...");
}
}
代码:
publicclass ConcreteProduct2 implements Product {
public ConcreteProduct2(){
System.out.println("do something in ConcreteProduct2...");
}
}
代码:
/**
*下面是具体工厂角色CocnreteCreator1的源代码。这个角色实
*现了抽象工厂角色Creator所声明的工厂方法。
*@authorxtgj
*/
publicclass ConcreteCreator1 implements Creator {
public Product factory() {
returnnew ConcreteProduct1() ;
}
}
代码:
publicclass ConcreteCreator2 implements Creator {
public Product factory() {
returnnew ConcreteProduct2() ;
}
}
代码:
/**
*客户端代码
*@authorxtgj
*/
publicclass Client {
privatestatic Creator creator1,creator2 ;
privatestatic Product prod1,prod2 ;
publicstaticvoid main(String[] args) {
creator1 = newConcreteCreator1() ;
prod1 = creator1.factory() ;
creator2 = new ConcreteCreator2() ;
prod2 = creator2.factory() ;
}
}
这个示意性的系统只给出了两个具体工厂类和两个具体产品类。在真实的系统中,工厂方法模式一般都会涉及到更多的具体工厂类和更多的具体产品类。
16.2.3抽象工厂
16.2.3.1 抽象工厂的用意
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个相似产品的产品对象。这就是抽象工厂模式的用意。
16.2.3.2 对抽象工厂的理解
抽象工厂模式涉及到以下的角色。
1、 抽象工厂(AbstractFactory)角色:
担任这个角色的是工厂方法模式的核心,它是及应用系统的商业逻辑无关的。通常使用 Java 接口或者抽象 Java 类实现,而所有的具体工厂类必须实现这个 Java 接口或继承这个抽象 Java 类。
2、 具体工厂类(Conrete Factory)角色:
这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是及应用系统的商业逻辑紧密相关的。通常使用具体 Java 类实现这个角色。
3、 抽象产品(Abstract Product)角色:
担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。通常使用 Java 接口或者抽象 Java 类实现这一角色。
4、 具体产品(Concrete Product)角色:
抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。通常使用具体 Java 类实现这个角色。
以工厂为例(如上图),工厂生产产品会分为不同类型,不同类型产品(如上图中:
ProductA和ProductB)中又有不同等级,就像本章前面所谈到的一样,如果使用工厂方
法模式处理的话,就必须要有两个独立的工厂等级(如上图中:ConcreteCreator1和
ConcreteCreator1)。由于这两个产品等级中的产品是类型相同的,因此,使用同一个工
厂等级也可以处理这两个产品等级的创建问题。
由于每个具体工厂角色都需要负责两个不同类型的产品对象的创建,因此每个
工厂角色都需要提供两个工厂方法,分别用于创建两种类型的产品。既然每个具体
工厂角色都需要实现这两个工厂方法,所以这种情况就具有一般性,不妨抽象出来,
移动到抽象工厂角色 Creator中加以声明。
代码:
/**
* 下面是产品类型 A的抽象产品角色,在这个示意性的系统中,
* 这个抽象产品角色是由一个Java 接口实现的。
* @author xtgj
*/
public interface ProductA {
public void product() ;
}
代码:
/**
* 下面是产品类型 B的抽象产品角色,这个抽象产品角色也是由一个
* Java 接口实现的。
* @author xtgj
*/
public interface ProductB {
public void product() ;
}
代码:
/**
* 下面是属于产品类型 A的具体产品类 ProductA1 的源代码。
* 这个具体产品实现了产品类型 A的抽象产品接口。
* @author xtgj
*/
public class ProductA1 implements ProductA {
public ProductA1(){
product() ;
System.out.println("ProductA1生产结束!");
}
public void product() {
System.out.println("ProductA1生产中...");
}
}
代码:
public class ProductA2 implements ProductA {
public ProductA2(){
product() ;
System.out.println("ProductA2生产结束!");
}
public void product() {
System.out.println("ProductA2生产中...");
}
}
代码:
public class ProductB1 implements ProductB {
public ProductB1(){
product() ;
System.out.println("ProductB1生产结束!");
}
public void product() {
System.out.println("ProductB1生产中...");
}
}
代码:
public class ProductB2 implements ProductB {
public ProductB2(){
product() ;
System.out.println("ProductB2生产结束!");
}
public void product() {
System.out.println("ProductB2生产中...");
}
}
代码:
/**
* 抽象工厂规定两个工厂方法,分别提供两个不同产品类型的产品对象
*1、一般而言,有多少个产品类型,就会在工厂角色中发现多少个工厂方法。
*2、每一个产品类型中有多少具体产品,就有多少个产品等级,
* 也就会在工厂等级中发现多少个具体工厂(如:ConcreteCreator1和
* ConcreteCreator2具体工厂类)。
* @author xtgj
*/
public interface Creator {
/**
* 产品类型A的工厂方法
*/
public ProductA factoryA() ;
/**
* 产品类型B的工厂方法
*/
public ProductB factoryB() ;
}
代码:
/**
* 创建具体工厂类ConcreteCreator1
* @author xtgj
*/
public class ConcreteCreator1 implements Creator {
/**
* 产品类型A的工厂方法
*/
public ProductA factoryA() {
System.out.println("准备生产ProductA1...");
return new ProductA1();
}
/**
* 产品类型B的工厂方法
*/
public ProductB factoryB() {
System.out.println("准备生产ProductB1...");
return new ProductB1();
}
}
代码:
/**
* 创建具体工厂类ConcreteCreator1
* @author xtgj
*/
public class ConcreteCreator1 implements Creator {
/**
* 产品类型A的工厂方法
*/
public ProductA factoryA() {
System.out.println("准备生产ProductA1...");
return new ProductA1();
}
/**
* 产品类型B的工厂方法
*/
public ProductB factoryB() {
System.out.println("准备生产ProductB1...");
return new ProductB1();
}
}
代码:
/**
* 创建具体工厂类ConcreteCreator2
* @author xtgj
*/
public class ConcreteCreator2 implements Creator {
/**
* 产品类型A的工厂方法
*/
public ProductA factoryA() {
System.out.println("准备生产ProductA2...");
return new ProductA2();
}
/**
* 产品类型B的工厂方法
*/
public ProductB factoryB() {
System.out.println("准备生产ProductB2...");
return new ProductB2();
}
}
代码:
/**
*客户端
*@authorxtgj
*
*/
publicclass Client {
publicstaticvoid main(String[] args) {
/**
*需要产品等级为ConcreteCreator1中的产品类型为
*ProductA的产品
*/
ConcreteCreator1 con1 = new ConcreteCreator1() ;
con1.factoryA() ;
}
}
16.2.3.3抽象工厂适用的环境
在以下情况下应当考虑使用抽象工厂模式:
(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节。这对于所有形态的工厂模式都是重要的;
(2)这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品;
(上面这一条叫做抽象工厂模式的原始用意。)
(3)同属于同一个产品族的产品是在一起使用的,这一约束必须要在系统的设计中体现出来;
(4)系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
16.2.3.4“开-闭”原则
“开-闭”原则要求一个软件系统可以在不修改原有代码的情况下,通过扩展达到增强其功能的目的。对于一个涉及到多个产品等级结构和多个产品族的系统,其功能的增强不外乎两个方面:
(1)增加新的产品类型;
(2)增加新的产品等级。
那么抽象工厂模式是怎样支持这两方面功能增强的呢?
1、增加新的产品类型
在产品等级的数目不变的情况下,增加新的产品类型,就意味着在每一个产品等级
中增加一个(或者多个)新的具体(或者抽象和具体)产品角色。
由于工厂等级是及产品等级平行的等级机构,因此,当产品等级有所调整时,需要
将工厂等级做相应的调整。现在产品等级中出现了新的元素,因此,需要向工厂等级中
加入相应的新元素就可以了。
换言之,设计师只需要向系统中加入新的具体工厂类就可以了,没有必要修改已有
的工厂角色或者产品角色。因此,在系统中的产品类型增加时,抽象工厂模式是支持“
开-闭”原则的。
代码:
/**
* 添加产品类型 C的抽象产品角色,之后如上再添加具体实现类
* @author xtgj
*/
public interface ProductC {
public void product() ;
}
/**
*在ConcreteCreator1和ConcreteCreator2中添加产品类型C的工厂方法
*/
public ProductC factoryC() {
System.out.println("准备生产ProductA1...");
returnnew ProductC1();
}
2、增加新的产品等级
在产品类型的数目不变的情况下,增加新的产品等级结构。
换言之,所有的产品等级结构中的产品数目不会改变,但是现在多出一个及现有的
产品等级结构平行的新的产品等级结构。
要做到这一点,就需要修改所有的工厂角色,给每一个工厂类都增加一个新的工厂
方法,而这显然是违背“开–闭”原则的。
换言之,对于产品等级结构的增加,抽象工厂模式是不支持“开–闭”原则的。
综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品的增
加提供方便,而不能为新的产品等级结构的增加提供这样的方便。
16.3单例(Singleton)模式
16.3.1单例模式的定义:
16.3.1.1定义:
Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。
16.3.1.2对定义的理解:
在很多操作中,比如建立目录、数据库连接都需要这样的单线程操作。还有, singleton能够被状态化;这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要实现论坛中的帖子计数器:每浏览一次都需要计数,单态类能否保持住这个计数,并且能synchronized的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。
另外方面,Singleton也能够被无状态化。提供工具性质的功能,Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。
我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。
16.3.1.3下面理解一个例子 Singleton类之所以是private型构造方法,就是为了防止其它类通过new来创建实例,即如此,那我们就必须用一个static的方法来创建一个实例(为什么要用static的方法?因为既然其它类不能通过new来创建实例,那么就无法获取其对象,那么只用有类的方法来获取了)
代码:
publicclass Singleton {
privatestatic Singleton instance ;
/**
*定义private类型的构造方法是为防止外部类实例化Singleton
*/
private Singleton(){}
/**
*在此创建公共静态方法getInstance()用来实例化Singleton类
*@returnSingleton
*/
publicstatic Singleton getInstance(){
System.out.println("进入getInstance");
if(instance==null){
System.out.println("Singleton被实例化");
instance = new Singleton() ;
}
System.out.println("退出getInstance");
returninstance ;
}
publicvoid say(){
System.out.println("单例模式");
}
}
下面模拟论坛中的帖子计数器:
代码:
publicclass Singleton {
//final在这个例子中是表示那个singleton在后面是不可以修改的
privatestaticfinal Singleton singleton = new Singleton() ;
privatestaticintseed = 0;
publicstatic Singleton getInstance(){
returnsingleton ;
}
publicint seedIncrease(){
//设置点击时自增
seed++ ;
returnseed ;
}
}
代码:
publicclass TestSeed {
publicvoid test(){
Singleton sing = null ;
for (int i = 0; i < 2; i++) {
//在此实例化Singleton
sing = Singleton.getInstance() ;
int seed = sing.seedIncrease() ;
System.out.println("点击次数"+seed);
}
}
}
代码:
publicclass Client {
publicstaticvoid main(String[] args) {
/**
*共有5个用户点击,每个用户实例化一次TestSeed
*但是Singleton的实例只有一个
*/
for (int i = 0; i < 5; i++) {
System.out.println("用户"+(i+1));
TestSeed test = new TestSeed() ;
test.test() ;
}
}
}
此写法是把实例化的对象定义成final类型的变
展开阅读全文