在这一次分享中,向大家介绍一下工厂模式。工厂模式简单的说就是将对象创建的细节封装起来,实现这样目的的方式有三种:简单工厂、工厂方法模式和抽象工厂模式。在介绍着几个方式之前,首先来向大家介绍一个设计原则—依赖倒置原则。
什么是依赖倒置原则呢?官方说:“要依赖抽象,不要依赖具体类”,这和针对接口编程很相似。通过一个例子让大家了解依赖倒置原则。
假设我要去披萨店点披萨,一共有3种披萨CheesePizza,VeggiePizza,ClamPizza。通过PizzaStore的orderPizza()方法来点披萨。代码是这样的。
import pizza.CheesePizza;
import pizza.ClamPizza;
import pizza.Pizza;
import pizza.VeggiePizza;
/**
* 披萨店
* @author wangpeiyu
*
*/
public class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza=null;
if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("veggie"))
{
pizza = new VeggiePizza();
}else if(type.equals("clam"))
{
pizza = new ClamPizza();
}
return pizza;
}
}
在UML中,将上述类间关系画出来是这样的。
可以看到PizzaStore类依赖Pizza及所有他的子类,这样导致依赖非常严重,高耦合。对于在上面的代码,你会发现,如果你要新增一种披萨,除了要新建一个Pizza子类外,还要在PizzaStore的orderPizza()方法中修改相应的代码,这样导致耦合度很高。
如果我们把orderPizza()方法中创建Pizza对象的代码块抽取出来,放到另外的一个对象中专门处理,如下面的代码。
PizzaStore类
import factory.PizzaFactory;
import pizza.CheesePizza;
import pizza.ClamPizza;
import pizza.Pizza;
import pizza.VeggiePizza;
/**
* 披萨店
* @author wangpeiyu
*
*/
public class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza=null;
/* if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("veggie"))
{
pizza = new VeggiePizza();
}else if(type.equals("clam"))
{
pizza = new ClamPizza();
}*/
pizza = PizzaFactory.createPizza(type);
/**
* 再执行其他的相关操作
*/
return pizza;
}
}
PizzaFactory工厂类
package factory;
import pizza.CheesePizza;
import pizza.ClamPizza;
import pizza.Pizza;
import pizza.VeggiePizza;
/**
* 披萨工厂类
* 专职处理披萨对象的创建
* 如果要创建一个披萨对象,直接找这个对象就可以了
* @author wangpeiyu
*
*/
public class PizzaFactory {
/**
* 生成具体的披萨对象
* @param type 披萨的类型
* @return 返回生成的指定披萨实例
*/
public static Pizza createPizza(String type)
{
Pizza pizza=null;
if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("veggie"))
{
pizza = new VeggiePizza();
}else if(type.equals("clam"))
{
pizza = new ClamPizza();
}
return pizza;
}
}
这时候PizzaStore的UML图是这样的。
你会发现依赖图原本由上而下的,现在倒置了。换句话说:不能让高层组件(PizzaStore,Pizza)依赖低层组件(Pizza子类),而且不管是高层还是低层组件,两者都应该依赖抽象。
上面将创建对象的代码块抽取出来,放到PizzaFactory中去实现的方式,我们成为简单工厂。这时候,如果你要增加一种披萨,PizzaStore中的所有代码都不需要更改,你只有修改专门负责创建pizza对象的PizzaFactory类中的createPizza方法就可以了。而且在其他任何地方用到创建Pizza对象也直接用PizzaFactory就可以了,这样即使需要修改也是修改一处,提高了代码的重用,降低了耦合。
下面来介绍一下另一种方式,工厂方法模式。
改变一下需求,上面说的三种披萨各有二种风味的,分别为纽约风味和伦敦风味。因此及现在Pizza的子类就有6个了。分别为
NYCheesePizza,LDCheesePizza,NYVeggiePizza,LDVeggiePizza,NYClamPizza,LDClamPizza.
传统我们是怎么实现的呢。
在PizzaStore类中的orderPizza()方法是代码这样的。
public Pizza orderPizza(String style,String type)
{
Pizza pizza = null;
if(style.equals("NewYork")){
if(type.equals("cheese")){
pizza=new NYCheesePizza();
}else if(type.equals("veggie"))
{
pizza = new NYVeggiePizza();
}else if(type.equals("clam"))
{
pizza = new NYClamPizza();
}
}else if(style.equals("London")){
if(type.equals("cheese")){
pizza=new LDCheesePizza();
}else if(type.equals("veggie"))
{
pizza = new LDVeggiePizza();
}else if(type.equals("clam"))
{
pizza = new LDClamPizza();
}
}
return pizza;
}
你会发现,判断特别的多。如果还要增加新的pizza或者风味,那这么判断就不可想象了,非常容易错误。经过思考,我们可以将PizzaStore作为基类,创建一个抽象的方法createPizza(),不同的风味的披萨的创建作为一个子类,那这个子类就必须实现createPizza()方法,这个方法只创建属于这个子类风味的披萨。这样要创建不同风味的披萨,则创建相应的子类对象赋值给PizzaStore基类引用,利用多态,PizzaStore引用可以指向不同的风味,则可以创建不同的风味披萨了,而且即使要增加新的风味,则继承PizzaStore实现该特定风味的披萨的createPizza()方法就可以了。
下面是代码的实现。
基类PizzaStore类的代码,PizzaStore类为抽象类,增加了一个抽象的方法createPizza(),子类必须要实现这个方法。
import factory.PizzaFactory;
import pizza.CheesePizza;
import pizza.ClamPizza;
import pizza.LDCheesePizza;
import pizza.LDClamPizza;
import pizza.LDVeggiePizza;
import pizza.NYCheesePizza;
import pizza.NYClamPizza;
import pizza.NYVeggiePizza;
import pizza.Pizza;
import pizza.VeggiePizza;
/**
* 披萨店
* @author wangpeiyu
*
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza=null;
/* if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("veggie"))
{
pizza = new VeggiePizza();
}else if(type.equals("clam"))
{
pizza = new ClamPizza();
}*/
//这个是简单工厂实现
//pizza = PizzaFactory.createPizza(type);
//下面是工厂方法模式实现的
pizza = createPizza(type);
/**
* 再执行其他的相关操作
*/
return pizza;
}
public abstract Pizza createPizza(String type);
/**
* 传统的实现不同风味的披萨创建
* @param style 风味
* @param type 披萨的类型
* @return 返回指定风味的指定披萨
*/
/*public Pizza orderPizza(String style,String type)
{
Pizza pizza = null;
if(style.equals("NewYork")){
if(type.equals("cheese")){
pizza=new NYCheesePizza();
}else if(type.equals("veggie"))
{
pizza = new NYVeggiePizza();
}else if(type.equals("clam"))
{
pizza = new NYClamPizza();
}
}else if(style.equals("London")){
if(type.equals("cheese")){
pizza=new LDCheesePizza();
}else if(type.equals("veggie"))
{
pizza = new LDVeggiePizza();
}else if(type.equals("clam"))
{
pizza = new LDClamPizza();
}
}
return pizza;
}*/
}
纽约风味的披萨的NYPizzaStore类代码,继承与PizzaStore类,必须实现PizzaStore类中的抽象方法createPizza().
import pizza.NYCheesePizza;
import pizza.NYClamPizza;
import pizza.NYVeggiePizza;
import pizza.Pizza;
/**
* 纽约风味的披萨店
* 专门创建纽约风味的pisa
* @author wangpeiyu
*
*/
public class NYPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")){
pizza=new NYCheesePizza();
}else if(type.equals("veggie"))
{
pizza = new NYVeggiePizza();
}else if(type.equals("clam"))
{
pizza = new NYClamPizza();
}
return pizza;
}
}
下面是伦敦风味的披萨类,与纽约的一致。
import pizza.LDCheesePizza;
import pizza.LDClamPizza;
import pizza.LDVeggiePizza;
import pizza.Pizza;
/**
* 伦敦风味的披萨
* 专门创建负责伦敦风味的披萨
* @author wangpeiyu
*
*/
public class LDPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza=null;
if(type.equals("cheese")){
pizza=new LDCheesePizza();
}else if(type.equals("veggie"))
{
pizza = new LDVeggiePizza();
}else if(type.equals("clam"))
{
pizza = new LDClamPizza();
}
return pizza;
}
}
下面来创建一个测试类,来测试工厂方法模式。
package test;
import pizza.LDClamPizza;
import store.LDPizzaStore;
import store.NYPizzaStore;
import store.PizzaStore;
public class main {
public static void main(String[] args) {
//新建一个PizzaStore基类引用
PizzaStore store=null;
/**
* 小明点了一个纽约风味的cheese披萨
*/
//赋值纽约风味的对象
store = new NYPizzaStore();
store.orderPizza("cheese").tostring();
/**
* 小红点了一个;伦敦风味的clam披萨
*/
//赋值纽约风味的对象
store = new LDPizzaStore();
store.orderPizza("clam").tostring();
}
}
输出的结果为:
NYCheesePizza
LDClamPizza
可以看出已经订了相应风味的执行披萨。
现在我想在生产披萨对象的时候指定原料的产地,我们假设原料产地有两个地方,纽约原料产地和伦敦原料产地,NYSourceFactory和LDSourceFactory类。为了满足针对接口编程的原则,我们要建立一个原料产地的基类。SourceFactory,在创建披萨的时候要将原料产地对象传递进去。现在将代码修改后为:
SourceFactory类
package factory;
/**
* 抽象工厂模式
* 原料工厂
* @author wangpeiyu
*
*/
public interface SourceFactory {
public String createDough();
public String createSauce();
}
下面是相应的原料工厂
纽约原料工厂:
package factory;
/**
* 纽约的原料产地
* @author wangpeiyu
*
*/
public class NYSourceFactory implements SourceFactory {
@Override
public String createDough() {
// TODO Auto-generated method stub
return "ny dough";
}
@Override
public String createSauce() {
// TODO Auto-generated method stub
return "ny sauce";
}
}
伦敦原料工厂
package factory;
/**
* 伦敦的原料产地
* @author wangpeiyu
*
*/
public class LDSourceFactory implements SourceFactory {
@Override
public String createDough() {
// TODO Auto-generated method stub
return "ld dough";
}
@Override
public String createSauce() {
// TODO Auto-generated method stub
return "ld sauce";
}
}
因为创建pizza要指定原料,所以pizza类中的材料要进行初始化,初始化为相应的原料。在不同的披萨中,添加的原料可能不同。所以在pizza中创建一个抽象的方法prepare,用来准备材料,在子类中必须实现这个方法,初始化相应的材料。
Pizza类
package pizza;
public abstract class Pizza {
/**
* 名称
*/
protected String name;
/**
* 面团类型
*/
protected String dough;
/**
* 酱料类型,这是一种佐料
*/
protected String sauce;
public void tostring()
{
System.out.println("Pizza");
}
public abstract void prepare();
}
下面是NYCheesePizza披萨
package pizza;
import factory.SourceFactory;
public class NYCheesePizza extends Pizza {
SourceFactory factory;
public NYCheesePizza(SourceFactory factory) {
this.factory = factory;
}
public void tostring()
{
System.out.println("NYCheesePizza " + dough+" "+sauce);
}
@Override
public void prepare() {
dough = factory.createDough();
sauce = factory.createSauce();
}
}
package pizza;
import factory.SourceFactory;
public class LDCheesePizza extends Pizza {
SourceFactory factory;
public LDCheesePizza(SourceFactory factory) {
this.factory = factory;
}
public void tostring()
{
System.out.println("LDCheesePizza " + dough+" "+sauce);
}
@Override
public void prepare() {
this.dough = factory.createDough();
this.sauce = factory.createSauce();
}
}
package pizza;
import factory.SourceFactory;
public class LDClamPizza extends Pizza {
SourceFactory factory;
public LDClamPizza(SourceFactory factory) {
this.factory = factory;
}
public void tostring()
{
System.out.println("LDClamPizza " + dough+" "+sauce);
}
@Override
public void prepare() {
dough = factory.createDough();
sauce =factory.createSauce();
}
}
package pizza;
import factory.SourceFactory;
public class LDVeggiePizza extends Pizza {
SourceFactory factory;
public LDVeggiePizza(SourceFactory factory) {
this.factory =factory;
}
public void tostring()
{
System.out.println("LDVeggiePizza " + dough+" "+sauce);
}
@Override
public void prepare() {
dough = factory.createDough();
sauce = factory.createSauce();
}
}
package pizza;
import factory.SourceFactory;
public class NYClamPizza extends Pizza {
SourceFactory factory;
public NYClamPizza(SourceFactory factory) {
this.factory = factory;
}
public void tostring()
{
System.out.println("NYClamPizza " + dough+" "+sauce);
}
@Override
public void prepare() {
dough =factory.createDough();
sauce= factory.createSauce();
}
}
package pizza;
import factory.SourceFactory;
public class NYVeggiePizza extends Pizza{
SourceFactory factory;
public NYVeggiePizza(SourceFactory factory) {
this.factory=factory;
}
public void tostring()
{
System.out.println("NYVeggiePizza " + dough+" "+sauce);
}
@Override
public void prepare() {
dough = factory.createDough();
sauce =factory.createSauce();
}
}
NYPizzaStore披萨店类
package store;
import factory.NYSourceFactory;
import pizza.NYCheesePizza;
import pizza.NYClamPizza;
import pizza.NYVeggiePizza;
import pizza.Pizza;
/**
* 纽约风味的披萨店
* 专门创建纽约风味的pisa
* @author wangpeiyu
*
*/
public class NYPizzaStore extends PizzaStore {
NYSourceFactory factory=new NYSourceFactory();
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")){
pizza=new NYCheesePizza(factory);
}else if(type.equals("veggie"))
{
pizza = new NYVeggiePizza(factory);
}else if(type.equals("clam"))
{
pizza = new NYClamPizza(factory);
}
return pizza;
}
}
伦敦披萨店类
package store;
import factory.LDSourceFactory;
import factory.NYSourceFactory;
import pizza.LDCheesePizza;
import pizza.LDClamPizza;
import pizza.LDVeggiePizza;
import pizza.Pizza;
/**
* 伦敦风味的披萨
* 专门创建负责伦敦风味的披萨
* @author wangpeiyu
*
*/
public class LDPizzaStore extends PizzaStore {
LDSourceFactory factory=new LDSourceFactory();
@Override
public Pizza createPizza(String type) {
Pizza pizza=null;
if(type.equals("cheese")){
pizza=new LDCheesePizza(factory);
}else if(type.equals("veggie"))
{
pizza = new LDVeggiePizza(factory);
}else if(type.equals("clam"))
{
pizza = new LDClamPizza(factory);
}
return pizza;
}
}
在这里规定了NYPizzaStore商店用的是NYSourceFactory工厂的原料。但是真正的抽象工厂模式不是在这里,而是在各个Pizza的子类中,通过SourceFactory的引用factory去动态的创建原料,这里是创建了两种材料,createDough和createSauce。
测试类:
package test;
import pizza.LDClamPizza;
import pizza.Pizza;
import store.LDPizzaStore;
import store.NYPizzaStore;
import store.PizzaStore;
public class main {
public static void main(String[] args) {
//新建一个PizzaStore基类引用
PizzaStore store=null;
Pizza pizza=null;
/**
* 小明点了一个纽约风味的cheese披萨
*/
//赋值纽约风味的对象
store = new NYPizzaStore();
pizza = store.orderPizza("cheese");
pizza.prepare();
pizza.tostring();
/**
* 小红点了一个;伦敦风味的clam披萨
*/
//赋值纽约风味的对象
store = new LDPizzaStore();
pizza = store.orderPizza("cheese");
pizza.prepare();
pizza.tostring();
}
}
测试的结果是:
NYCheesePizza ny dough ny sauce
LDCheesePizza ld dough ld sauce
已经实现了动态的生成原料。
工厂方式模式和抽象工厂模式其实相似,但是也有不同。不同在于工厂方法模式是基于基类派生出来的,而且只能创建一个对象。而抽象工厂模式则是生产一系列的产品族,在这个例子中是生产了dough和sauce原料,而且是在其他的对象中,通过组合来实现的。
总而言之,工厂模式就是将对象的创建细节封装起来。工厂方法模式是将对象的创建置于子类中实现,让子类决定创建哪些实例。抽象工厂模式则是将对象的创建置于工厂对象中,可以利用多态创建相应的产品族。