JAVA基础(五)工厂模式及其他创建型设计模式总结

既然已经开了创建型模式的坑,那就先把HEAD FIRST里面的创捷模式说完,下面就轮到工厂模式和抽象工厂模式了。
一、工厂模式:
先来看看工厂模式Factory Method的定义:
工厂模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
很多人都认为工厂模式只是把创建对象的工作进行封装,这并不算错,不过工厂模式的关键在于将实例化推迟到子类。单纯的实例化封装还是实例化推迟正是简单工厂模式与工厂模式的分界(前者甚至在HEAD FIRST中都不能称为模式)。
首先介绍简单工厂:
public class SimpleFactory{	//简单的建了一个类来实现根据输入返回对象的功能
	public Pizza creatpizza(String s){
		Pizza pizza = null;
		if(s.equals("A")){
			pizza = new Apizza();
		}
		else if(s.equals("B")){
			pizza = new Bpizza();
		}
		return pizza;
	}
}
public class PizzaStore {
	SimpleFactory sf = new SimpleFactory();	//造个简单工厂
	Pizza pizza = sf.creatpizza("A");	//调用方法,大功告成~~~
}

然后是具备延迟实例化的工厂:
public abstract class PizzaStore {
	public abstract Pizza createPizza(String s);	//和简单工厂的差别仅仅在于函数是否抽象
}
public class APizzaStore extends PizzaStore{	//抽象类的实现1
	public Pizza createPizza(String s){
		Pizza pizza = null;
		if(s.equals("A")){
			pizza = new AApizza();
		}
		else if(s.equals("B")){
			pizza = new ABpizza();
		}
		return pizza;
	}
}
public class BPizzaStore extends PizzaStore{	//抽象类的实现2
	public Pizza createPizza(String s){
		Pizza pizza = null;
		if(s.equals("A")){
			pizza = new BApizza();
		}
		else if(s.equals("B")){
			pizza = new BBpizza();
		}
		return pizza;
	}
}	//不同的PizzaStore可以创建不同的Pizza种类了

可以看到,推迟实例化可以使不同的工厂产生出完全不同的产品,所谓的工厂也不是一定要把方法单独封装为一个类,他可以仅仅是一个待实现的抽象的方法,这是简单工厂模式所完全不能比拟的

二、抽象工厂
抽象工厂Abstract Factory:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
抽象工厂从名字上看似乎和工厂没什么差别,不过,在实际应用中,抽象工厂是工厂的进一步深化,以PIZZA为例,抽象工厂能够在PIZZA种类相同的情况下提供不同的方法效果,因为这些方法的实现需要依赖某些对象。下面用代码来简单说明:
假设Pizza中有一个prepare方法,将该方法调用某个工厂对象中:
public abstract class Pizza{
	public void prepare(Factory f){
		f.prepare();
	}
}
这时引入抽象工厂:
public abstract class Factory{
	public abstract void prepare();
}
public class AFactory extends Factory{		//抽象工厂A
	public void prepare(){
		System.out.print("A prepare!");
	}
}
public class BFactory extends Factory{		//抽象工厂B
	public void prepare(){
		System.out.print("B prepare!");
	}
}
我们建立一个正式工厂,用以调用抽象工厂的方法
public class APizzaStore extends PizzaStore{
	Factory f = new AFactory();		//在工厂中实例化了抽象工厂
	public Pizza createPizza(String s){
		Pizza pizza = null;
		if(s.equals("A")){
			pizza = new AApizza();
		}
		else if(s.equals("B")){
			pizza = new ABpizza();
		}
		pizza.prepare(f);
		return pizza;
	}
}
可以看见,抽象工厂和工厂模式是可以并存的,我们既可以通过修改createPizza方法生成完全不同的对象,也可以在对象相同的情况下添加不同的行为,以生成不同状态的同类对象

下面把三种工厂再总结一下:简单工厂只是单纯的封装创捷过程到类,工厂将创建方法的生成延迟到子类实例化,而抽象工厂则通过抽象化产品内部依赖的对象,实现同种产品效果的差异化
三、其它模式(静态工厂,构建器,原型)
下面的内容HEAD FIRST只是简略介绍,不过既然在EFFECTIVE JAVA里面(第1,2条)有明确描述,和对象创建相关度又高,在这里就一并总结了。
1、静态工厂
静态工厂,其实就是将对象的生成作为静态方法返回,由于静态方法不能是抽象的,所以该方法一般应用于简单工厂模式。准确来说静态工厂不能称之为一个模式,但是由于Effective Java的第一条里面有专门的描述,所以在这里简单总结一下其优势:
a.静态方法有名字,可以生成任意多种类别的对象而能明确区分
b.不必每次都返回一个新对象,这在单例模式或者某些为了节约性能的情况下尤其有用
c.可以返回对象的任意子类,而不需要知道子类的细节。这一条有点抽象,事实上enum,数据库connection接口,Collection的API中已经大量使用此种方法。通过将获取对象的方法置于对象外,API的呈现将变得极为简洁
d.参数化类型实例的简化,考虑如下代码:
Map<String,List<String>> m1 = new HashMap<String,List<String>>();	//重写两次类型
public static <K,V> HashMap<K,V> newInstance(){
	return new HashMap<K,V>();
}
Map<String,List<String>> m2 = HashMap.newInstance();			//静态工厂导致类型推导,代码更加简洁
而静态工厂类型推导能力使得变量生成变得非常友好:
静态工厂方法的缺点在于,没有公有或受保护构造器的情况下不能子类化,且与别的静态方法没有本质区别。然而,瑕不掩瑜,第一个问题使得静态工厂方法鼓励程序员多用组合少用继承,这符合Effective Java后续的观点(16条),第二个问题也可以通过命名方式来进行区分,总的来说,静态工厂方法是非常值得提倡的获取对象的方法。

2.构建器模式Builder:
先来看看HEAD FIRST的构建器定义:封装一个产品的构造过程,并允许按步骤构造。
这个描述和Effective Java(第2条)相比并没有将其长处展现出来,构建器的强大在于能够将对象创建扩展到大量的可选参数,考虑下面的对象定义代码。
public class NutritionFacts{
	private final int servingSize;
	private final int servings;
	
	private final int calories;
	private final int fat;
	private final int sodium;
	private final int carbohydrate;
	
	public static class Builder{	//静态内部类参与对象创建
		//Required parameters
		private final int servingSize;
		private final int servings;
		
		private int calories=0;
		private int fat=0;
		private int sodium=0;
		private int carbohydrate=0;
		
		public Builder(int servingSize,int servings){
			this.servingSize=servingSize;
			this.servings=servings;
		}
		public Builder calories(int val)
		{calories = val; return this;}
		public Builder fat(int val)
		{fat = val; return this;}
		public Builder sodium(int val)
		{sodium = val; return this;}
		public Builder carbohydrate(int val)
		{carbohydrate = val; return this;}
		public NutritionFacts build(){
			return new NutritionFacts(this);
		}
	}
	private NutritionFacts(Builder builder){	//直接通过构建器生成新的对象
		servingSize = builder.servingSize;
		servings = builder.servings;
		calories = builder.calories;
		fat = builder.fat;
		sodium = builder.sodium;
		carbohydrate = builder.carbohydrate;
	}
}
这时我们进行对象创建,可以执行以下代码:
NutritionFacts nf = new NutritionFacts.Builder(5, 5).calories(5).fat(5).sodium(5);
这种方式的优点有两方面:
a.与传统构造函数相比,可选参数的构造函数更加灵活,大幅降低构造函数的个数,且不易出错(尤其在参数数据类型相同的时候)
b.与JavaBean的set方法相比,我们在完成初始化前,对象是不可见的,这也从多线程上保证了对象的安全性,同时,也让我们创建不可变的对象成为可能(本例的变量就是不可变的)

3.原型模式Prototype Pattern
这个模式在Head First中着墨不多,和工厂模式相比,原型模式将所有需要用到的对象提前进行创建,并通过clone()函数在每次调用的时候提供一个备份(比如游戏里面反复出现的怪兽)

创建型模式到这里就基本介绍完了,往后将开始行为类模式的介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值