Java 设计模式(二)创建型模式

  本系列文章共分为五篇:
    设计模式的分类与原则
    创建型模式
    结构型模式
    行为型模式
    设计模式的区别
  本篇将介绍创建者模式,创建型模式的主要关注点是“怎样创建对象,使对象的创建与使用分离开来”。

一、单例模式

1.1 单例模式定义

  指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

1.2 单例模式特点

  单例类只有一个实例对象。
  该单例对象必须由单例类自行创建。
  单例类对外提供一个访问该单例的全局访问点。

1.3 单例模式主要角色

  单例类:包含一个实例且能自行创建这个实例的类。
  访问类:使用单例的类。

1.4 单例模式实现方式

  单例模式的实现方式较多,主要方式如下:

1.4.1懒汉式单例

  该模式的特点是类加载时没有生成单例,只有当第一次调用getlnstance方法时才去创建这个单例。代码示例如下:

public class Singleton{
    //保证instance在所有线程中同步
    private static volatile Singleton instance=null; 
    //private避免类在外部被实例化   
    private Singleton(){}                               
    public static synchronized Singleton getInstance(){
        //getInstance方法前加同步
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

  如果编写的是多线程程序,则不能删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

1.4.2 饿汉式单例

  饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。代码示例如下:

public class Singleton {  
    //在调用getInstance之前就创建一个static对象
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
        return instance;  
    }  
}
1.4.3 双重校验锁单例

  该方式既不会在类加载时就初始化单例对象,又合理缩小了synchronized锁的影响范围,安全且在多线程情况下能保持高性能。代码示例如下:

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}
1.4.4 静态内部类单例

  该方式通过创建一个静态内部类,在静态内部类中实例化单例,进而达到了延迟实例化的目的。因为外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化单例,故而不占内存。同时,静态内部类中final修饰单例,也达到了安全的目的。代码示例如下:

public class Singleton {  
    private static class SingletonHolder {  
         private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
         return SingletonHolder.INSTANCE;  
    }  
}

1.5 单例模式应用场景

  单例模式通常适用的场景:
   1)某类只要求生成一个对象的时候。
   2)当对象需要被共享的场合。由於单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。
   3)当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

二、工厂方法模式

  被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式"。本节介绍的“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品。

2.1 工厂方法模式定义

  定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。

2.2 工厂方法模式特点

  优点:
   1)只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
   2)在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则。
  缺点:
   每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
  工厂方法的缺点可以通过和反射结合来改进

2.3 工厂方法模式主要角色

  抽象工厂:提供了创建产品的接口。
  具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

2.4 工厂方法模式实现方式

  工厂方法模式的实现,以播放不同公司的不同类型电影为例。产品有两种:动作电影和喜剧电影,工厂也有两种:Fox公司和WBP公司,从不同的工厂里可以生成不同类型的电影,从而达到多样产品类型的目的。代码示例如下:

//抽象产品:电影
public interface  Movie {
	void play();
}

//具体产品1:动作电影
public class ActionMovie implements Movie {
	private String factory;
	public ActionMovie(String factory){
      this.factory = factory;
	}
	public void play() {
		System.out.println(factory+" ActionMovie is playing");	
	}
}

//具体产品2:喜剧电影
public class ComedyMovie implements Movie {
	private String factory;
	public ComedyMovie(String factory){
		this.factory = factory;
	}
	public void play() {
		System.out.println(factory+" ComedyMovie is playing");
	}
}

//抽象工厂
public interface Factory {
    Movie getActionMovie();
    Movie getComedyMovie();
}

//具体工厂1:Fox公司
public class FoxFactory implements Factory{
    public Movie getActionMovie(){  return new ActionMovie("Fox");  }
    public Movie getComedyMovie(){	return new ComedyMovie("Fox");  }
}

//具体工厂2:WBP公司
public class WBPFactory implements Factory{
    public Movie getActionMovie(){	return new ActionMovie("WBP");  }
    public Movie getComedyMovie(){	return new ComedyMovie("WBP");  }
}

//测试类
public class FactoryTest {
    public static void main(String[] args) {  
    	Factory foxFactory = new FoxFactory();  
        Movie actionMovie = foxFactory.getActionMovie();  
        actionMovie.play();  
        Movie comedyMovie = foxFactory.getComedyMovie();  
        comedyMovie.play(); 
    	Factory WBPFactory = new WBPFactory();  
        actionMovie = WBPFactory.getActionMovie();  
        actionMovie.play();  
        comedyMovie = WBPFactory.getComedyMovie();  
        comedyMovie.play(); 
    }  
}

  结果示例:

Fox ActionMovie is playing
Fox ComedyMovie is playing
WBP ActionMovie is playing
WBP ComedyMovie is playing

2.5 工厂方法模式应用场景

  工厂方法模式通常适用的场景:
   1)客户只知道创建产品的工厂名,而不知道具体的产品名。
   2)创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
   3)客户不关心创建产品的细节,只关心产品的品牌。

三、抽象工厂模式

  上一节的工厂方法模式只能产生一类产品,如果需要产生多类产品,就要使用抽象工厂模式。

3.1 抽象工厂模式定义

  一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

3.2 抽象工厂模式特点

  优点:
   1)可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
   2)当增加一个新的产品族时不需要修改原代码,满足开闭原则。
  缺点:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

3.3 抽象工厂模式主要角色

  抽象工厂:提供了创建产品的接口,可以创建多个不同类别的产品。
  具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

3.4 抽象工厂模式实现方式

  抽象工厂模式的实现,此处依然以影视作品为例,不同的产品为:电视剧和电影,工厂为不同的公司福克斯公司和华纳兄弟公司。和上个章节的功能相比,多了生成“电视剧”产品的功能。代码示例如下:

//抽象产品1:电影
public interface  Movie {
	void play();
}

//具体产品1:动作电影
public class ActionMovie implements Movie {
	private String factory;
	public ActionMovie(String factory){
      this.factory = factory;
	}
	public void play() {
		System.out.println(factory+" ActionMovie is playing");	
	}
}

//具体产品2:喜剧电影
public class ComedyMovie implements Movie {
	private String factory;
	public ComedyMovie(String factory){
		this.factory = factory;
	}
	public void play() {
		System.out.println(factory+" ComedyMovie is playing");
	}
}

//抽象产品2:电视剧
public interface TVSeries {
	void play();
}

//具体产品2:动作电视剧
public class ActionTVSeries  implements TVSeries{
	private String factory;
	public ActionTVSeries(String factory){
      this.factory = factory;
	}
	public void play() {
		System.out.println(factory+" ActionTVSeries is playing");	
	}
}

//具体产品2:喜剧电视剧
public class ComedyTVSeries implements TVSeries{
	private String factory;
	public ComedyTVSeries(String factory){
      this.factory = factory;
	}
	public void play() {
		System.out.println(factory+" ComedyTVSeries is playing");	
	}
}

//抽象工厂
public interface Factory {
    Movie getActionMovie();
    Movie getComedyMovie();
    TVSeries getActionTVSeries();
    TVSeries getComedyTVSeries();
}

//具体工厂1:20世纪 福克斯公司
public class FoxFactory implements Factory{
    public Movie getActionMovie(){   return new ActionMovie("Fox");  }
    public Movie getComedyMovie(){   return new ComedyMovie("Fox");  }
	public TVSeries getActionTVSeries() {	return new ActionTVSeries("Fox");  }
	public TVSeries getComedyTVSeries() {	return new ComedyTVSeries("Fox");  }
}

//具体工厂2:华纳兄弟公司
public class WBPFactory implements Factory{
    public Movie getActionMovie(){	return new ActionMovie("WBP");   }
    public Movie getComedyMovie(){	return new ComedyMovie("WBP");   }
	public TVSeries getActionTVSeries() {	return new ComedyTVSeries("WBP");  }
	public TVSeries getComedyTVSeries() {	return new ComedyTVSeries("WBP");  }
}

//测试类
public class FactoryTest {
    public static void main(String[] args) {  
    	Factory foxFactory = new FoxFactory();  
        Movie actionMovie = foxFactory.getActionMovie();  
        actionMovie.play();  
        Movie comedyMovie = foxFactory.getComedyMovie();  
        comedyMovie.play(); 
        TVSeries actionTVSeries = foxFactory.getActionTVSeries();
        actionTVSeries.play();
        TVSeries comedyTVSeries = foxFactory.getComedyTVSeries();
        comedyTVSeries.play();
    	Factory WBPFactory = new WBPFactory();  
        actionMovie = WBPFactory.getActionMovie();  
        actionMovie.play();  
        comedyMovie = WBPFactory.getComedyMovie();  
        comedyMovie.play(); 
        actionTVSeries = WBPFactory.getActionTVSeries();
        actionTVSeries.play();
        comedyTVSeries = WBPFactory.getComedyTVSeries();
        comedyTVSeries.play();
    }  
}

  结果示例:

Fox ActionMovie is playing
Fox ComedyMovie is playing
Fox ActionTVSeries is playing
Fox ComedyTVSeries is playing
WBP ActionMovie is playing
WBP ComedyMovie is playing
WBP ComedyTVSeries is playing
WBP ComedyTVSeries is playing

3.5 抽象工厂模式应用场景

  抽象工厂模式通常适用的场景:
   1)当需要创建的对象是一系列相互关联或相互依赖的产品族时。
   2)系统中有多个产品族,但每次只使用其中的某一族产品。
   3)系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

四、建造者模式

  建造者模式常常用来创建复杂的产品,此模式中,会有一个专业的建造者(Build)类负责复杂产品的具体创建过程,从而达到对象创建和使用分离的目的。

4.1 建造者模式定义

  指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。

4.2 建造者模式特点

  优点:
   1)各个具体的建造者相互独立,有利于系统的扩展。
   2)客户端不必知道产品内部组成的细节,便于控制细节风险。
  缺点:
   1)产品的组成部分必须相同,这限制了其使用范围。
   2)如果产品的内部变化复杂,该模式会增加很多的建造者类。

4.3 建造者模式主要角色

  产品:包含多个组成部件的复杂对象。
  抽象建造者:它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法。
  具体建造者:实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
  指挥者:它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

4.4 建造者模式实现方式

  此处以一个较复杂的产品创建过程为例,该产品包括partA、partB、partC三个模块,这三个模块需要分开创建,代码示例如下:

//包含复杂组件的产品类
class Product{
    private String partA;
    private String partB;
    private String partC;
    public void setPartA(String partA){
        this.partA=partA;
    }
    public void setPartB(String partB){
        this.partB=partB;
    }
    public void setPartC(String partC){
        this.partC=partC;
    }
    public void show(){
    	System.out.println("partA:"+partA+"\npartB:"+partB+"\npartC:"+partC);	
    }
}

//抽象建造者,一般定义各个组件的建造过程和返回对象接口
public abstract class Builder{
    //创建产品对象
    protected Product product=new Product();
    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract void buildPartC();
    //返回产品对象
    public Product getResult(){
        return product;
    }
}

//具体建造者
public class ConcreteBuilder extends Builder{
    public void buildPartA(){
        product.setPartA("AAA");
    }
    public void buildPartB(){
        product.setPartB("BBB");
    }
    public void buildPartC(){
        product.setPartC("CCC");
    }
}

//指挥者
public class Director{
    private Builder builder;
    public Director(Builder builder){
        this.builder=builder;
    }
    //产品构建与组装方法
    public Product construct(){
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
}

//测试类
public class BuildTest{
    public static void main(String[] args){
        Builder builder=new ConcreteBuilder();
        Director director=new Director(builder);
        Product product=director.construct();
        product.show();
    }
}

  结果示例:

partA:AAA
partB:BBB
partC:CCC

4.5 建造者模式应用场景

  建造者模式通常适用的场景:
   1)创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
   2)创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。

五、原型模式

  在有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,此时就需要用原型模式来创建对象。

5.1 原型模式定义

  用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

5.2 原型模式特点

  优点:
   1)性能提高。
   2)逃避构造函数的约束。
  缺点:
   1)对于现有类,进行克隆可能实现起来较为复杂。

5.3 原型模式主要角色

  抽象原型类:规定了具体原型对象必须实现的clone接口。
  具体原型类:实现抽象原型类的clone方法,它是可被复制的对象。
  访问类:使用具体原型类中的clone方法来复制新的对象。

5.4 原型模式实现方式

  原型模式的实现,有两种方式:深克隆与浅克隆。浅克隆是创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址;深克隆是创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。代码示例如下:

5.4.1 浅克隆实现
//抽象原型,原生的Cloneable接口
public interface Cloneable {
}
//具体原型
public class ConcretePrototype implements Cloneable {
    private String name;
    private int id;
    public Object clone(){
        ConcretePrototype prototype = null;
		try {
			prototype = (ConcretePrototype)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
        return prototype;
    }
    public String getName() {    return name;    }
    public void setName(String name) {   
        this.name = name;    
    }
	public int getId() {	return id;   }
	public void setId(int id) {
		this.id = id;
	}
}

//访问类&测试类
public class PrototypeTest {
    public static void main(String[] args) {  
    	ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("exp");
        ConcretePrototype prototype2 = (ConcretePrototype) prototype.clone();
    	System.out.println("prototype is equals prototype2?"+prototype.equals(prototype2));
    }
}

  结果示例:

prototype is equals prototype2?false
5.4.2 深克隆实现

  深克隆需要用序列化的方式来实现,即要实现Serializable接口。示例代码如下:

/*具体原型*/
public class ConcretePrototype implements Serializable {
    private String name;
    private int id;
    public ConcretePrototype clone(){
    	//将对象写入流中
    	ByteArrayOutputStream bao = new ByteArrayOutputStream();
    	ObjectOutputStream oos;
    	ObjectInputStream ois;
		try {
			oos = new ObjectOutputStream(bao);
			oos.writeObject(this);
	    	//将对象取出来
	    	ByteArrayInputStream bi = new ByteArrayInputStream(bao.toByteArray());
	    	ois = new ObjectInputStream(bi);
	    	try {
				return (ConcretePrototype)ois.readObject();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
    }
    public String getName() {    return name;   }
    public void setName(String name) {
        this.name = name;
    }
	public int getId() {	return id;    }
	public void setId(int id) {
		this.id = id;
	}
}
/*测试类*/
public class PrototypeTest {
    public static void main(String[] args) {  
    	ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("exp");
        prototype.setId(1);
        ConcretePrototype prototype2 = (ConcretePrototype) prototype.clone();
    	System.out.println("prototype is equals prototype2?"+prototype.equals(prototype2));
    	System.out.println("prototype2.getName():"+prototype2.getName()+",prototype2.getId():"+prototype2.getId());
    }
}

  结果示例:

prototype is equals prototype2?false
prototype2.getName():exp,prototype2.getId():1

5.5 原型模式应用场景

  原型模式通常适用的场景:
   1)对象之间相同或相似,常见的情况是:只有几个属性不同。
   2)对象的创建过程比较麻烦,但复制比较简单的时候。
  参考资料:创建型模式的特点和分类

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章