Bootstrap

Java设计模式之工厂模式

工厂模式是一种创建型模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类。当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时,就需要用到工厂模式。

在不使用工厂模式时,对象的创建和使用没有分离,都是在同一类中完成的。使用工厂模式之后,将对象的创建放在工厂类中,在客户端类中只需要调用工厂类的方法创建对象,然后使用对象,而无需关注对象创建的细节。因此,工厂模式完美的实现了对象创建和使用的分离。

1、简单工厂模式

简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。简单工厂模式结构比较简单,其核心是工厂类的设计。

在简单工厂模式中包含以下三个角色:

● Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product。

● Product(抽象产品角色):它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。

● ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。

在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象,它是工厂模式家族中最简单的一员。

//手机接口
interface Phone{
    void call();
}

//小米手机
public class XiaoMiPhone implements Phone{
    public void call(){
        System.out.println(“The phone of xiaomi is calling.”);
    }
}

//华为手机
public class HuaWeiPhone implements Phone{
    public void call(){
        System.out.println(“The phone of huawei is calling.”);
    }
}
//手机工厂
public class PhoneFactory{
	public Phone createPhone(String phoneType){
		if(StringUtils.isEmpty(phoneType)){
			throw new Exception(“手机类型不能为空!”);
		}
		if(phoneType.equals(“xiaomi”)){
			return new XiaoMiPhone();
		}else if(phoneType.equals(“huawei”)){
			return new HuaWeiPhone();
		}else{
			throw new Exception(“手机类型不存在!”);
		}
	}
}
//客户端代码
public class SimpleFactoryTest{
	public static void main(String[] args){
		PhoneFactory factory = new PhoneFactory();
		Phone phone = factory.createPhone(“xiaomi”);
		phone.call();
	}
}

简单工厂模式的缺点:如果需要新增苹果手机,就需要修改工厂类的代码,判断手机类型是否为苹果类型的,违反了开放封闭原则(对扩展开放,对修改封闭)。可以使用工厂方法模式克服这个缺点。

2、工厂方法模式

在简单工厂模式中只提供一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它需要知道每一个产品对象的创建细节,并根据参数决定实例化哪一个产品类。简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,需要在其中加入必要的业务逻辑,这违背了“开闭原则”。此外,在简单工厂模式中,所有的产品都由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。

在工厂方法模式中,不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

工厂方法模式(Factory Method Pattern):定义一个用于创建产品对象的工厂接口,让子类决定创建哪一个产品类的对象。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。

在工厂方法模式中包含以下四个角色:

● Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。

● ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。

● Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。

● ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类。

在简单工厂模式的基础上对代码稍加修改,手机接口、小米手机和华为手机的代码无需修改。

//手机接口
interface Phone{
    void call();
}

//小米手机
public class XiaoMiPhone implements Phone{
    public void call(){
        System.out.println(“The phone of xiaomi is calling.”);
    }
}

//华为手机
public class HuaWeiPhone implements Phone{
    public void call(){
        System.out.println(“The phone of huawei is calling.”);
    }
}

//苹果手机
public class IPhone implements Phone{
	public void call(){
		System.out.println(“The phone of IPhone is calling.”);
	}
}
//工厂接口
interface Factory{
	Phone createPhone();
}

//小米工厂
public class XiaoMiFactory implements Factory{
	public Phone createPhone(){
		return new XiaoMiPhone();
	}
}

//华为工厂
public class HuaWeiFactory implements Factory{
	public Phone createPhone(){
		return new HuaWeiPhone();
	}
}

//苹果工厂
public class IPhoneFactory implements Factory{
	public Phone createPhone(){
		return new IPhone();
	}
}
//客户端代码
public class SimpleFactoryTest{
	public static void main(String[] args){
		Factory factory = new XiaoMiFactory();
		Phone phone = factory.createPhone();
		phone.call();
	}
}

工厂方法模式的优点:如果需要新增苹果手机(新的产品),只需要编写一个苹果工厂实现工厂接口,无需修改工厂接口的代码,满足开放关闭原则,解决了简单工厂模式存在的问题,利于维护和扩展。缺点是每新增一种类型的手机(产品),就需要编写一个工厂实现类,加大了代码的编写量,增大了代码的冗余度。

在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销

3、抽象工厂模式

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,可以考虑将一些相关的产品组成一个产品族,由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有唯一性,一般情况下,一个具体工厂中只有一个或者一组重载的工厂方法。但是有时候希望一个工厂可以提供多个产品对象,而不是单一的产品对象,如一个电器工厂可以生产电视机、电冰箱、空调等多种电器,而不是只生产某一种电器。为了更好地理解抽象工厂模式,先引入两个概念:

(1)产品等级结构产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

(2)产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

产品等级结构与产品族示意图如下图所示:

上图是不同品牌的冰箱、电视机和空调,图中不同颜色的冰箱、电视和空调分别构成了三个不同的产品等级结构,而相同颜色的冰箱、电视和空调构成了一个产品族,每一个电器对象都位于某个产品族,并属于某个产品等级结构。上图中一共有三个产品族,分属于三个不同的产品等级结构,只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一确定这个产品。

当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。

在上图中,每一个具体工厂可以生产属于一个产品族的所有产品,例如生产颜色相同的冰箱、电视和空调,所生产的产品又位于不同的产品等级结构中。如果使用工厂方法模式,上图所示结构需要提供9个具体工厂,而使用抽象工厂模式只需要提供3个具体工厂,极大减少了系统中类的个数。

抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。

在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族。

在抽象工厂模式中包含以下四个角色:

● AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。

● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

● AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。

● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类。

在本例中,手机和耳机是一个产品族,手机接口和小米手机、华为手机是一个产品等级结构。

//手机接口
interface Phone{
	void call();
}
//小米手机
public class XiaoMiPhone implements Phone{
	public void call(){
		System.out.println(“The phone of xiaomi is calling.”);
	}
}
//华为手机
public class HuaWeiPhone implements Phone{
	public void call(){
		System.out.println(“The phone of huawei is calling.”);
	}
}
//耳机接口
interface Earphone{
	void hear();
}
//小米耳机
public class XiaoMiEarphone implements Earphone{
	public void hear(){
		System.out.println(“The earphone of XiaoMi is used.”);
	}
}
//华为耳机
public class HuaWeiEarphone implements Earphone{
	public void hear(){
		System.out.println(“The earphone of HuaWei is used.”);
	}
}
//工厂接口
interface Factory{
	Phone createPhone();
	Earphone createEarphone();
}
//小米工厂
public class XiaoMiFactory implements Factory{
	public Phone createPhone(){
		return new XiaoMiPhone();
	}
	public Earphone createEarphone(){
		return new XiaoMiEarphone();
	}
}
//华为工厂
public class HuaWeiFactory implements Factory{
	public Phone createPhone(){
		return new HuaWeiPhone();
	}
	public Earphone createEarphone(){
		return new HuaWeiEarphone();
	}
}
//客户端代码
public class SimpleFactoryTest{
	public static void main(String[] args){
		Factory factory = new XiaoMiFactory();
		Phone phone = factory.createPhone();
		Earphone earphone = factory.createEarphone();
		phone.call();
		earphone.hear();
	}
}

如果需要新增一个产品族,如苹果手机、苹果耳机,只需新增如下几个类即可,无需修改源代码。

//苹果手机
public class IPhone implements Phone{
	public void call(){
		System.out.println(“The phone of IPhone is calling.”);
	}
}

//苹果耳机
public class AppleEarphone implements Earphone{
	public void hear(){
		System.out.println(“The earphone of Apple is used.”);
	}
}

//苹果工厂
public class IPhoneFactory implements Factory{
	public Phone createPhone(){
		return new IPhone();
	}
	public Earphone createEarphone(){
		return new AppleEarphone();
	}
}

产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。例如,小米工厂生产的小米手机和小米耳机构成了一个产品族,小米手机位于手机产品等级结构中,小米耳机位于耳机产品等级结构中。

在抽象工厂模式中,增加一个产品族很简单,而增加一个新的产品会非常复杂。例如,增加苹果手机、苹果耳机,可以直接让苹果手机和苹果耳机分别实现手机接口和耳机接口,增加一个苹果工厂实现工厂接口,这样不会对原来的代码有任何的侵入操作,完美的利用了开放封闭原则。但是,如果增加一个手机充电器(即新增一个产品等级结构),需要新增一个充电器接口和小米充电器、华为充电器两个实现类,同时需要修改工厂接口和小米工厂、华为工厂的代码逻辑,在工厂接口中新增与充电器有关的方法,在小米工厂和华为工厂这两个实现类中新增处理充电器的逻辑,违反了开放封闭原则。

在工厂方法模式中,一个工厂只对应一个产品,工厂实现类中的方法都是与这一个产品有关的。在抽象工厂模式中,一个工厂对应一个产品族,工厂实现类中的方法是与产品族中的多个产品有关的。一个产品也可以看成是一个产品族,只不过这个产品族中只有一个产品,所以,可以将工厂方法模式看成是抽象工厂模式的简化版本。在工厂方法模式和抽象工厂模式中,如果新增一个产品等级结构,都会修改原来的工厂接口以及工厂实现类的代码,违反了开放封闭原则。此时可以使用简单工厂模式,新增一个产品类和该产品对应的工厂类即可,不会破坏原代码的封闭性。因此,当需要新增一个产品等级结构时,使用简单工厂模式,当需要新增一个产品族时,使用抽象工厂模式。

练习

Sunny软件公司欲推出一款新的手机游戏软件,该软件能够支持Symbian、Android和Windows Mobile等多个智能手机操作系统平台,针对不同的手机操作系统,该游戏软件提供了不同的游戏操作控制(OperationController)类和游戏界面控制(InterfaceController)类,并提供相应的工厂类来封装这些类的初始化过程。软件要求具有较好的扩展性以支持新的操作系统平台,为了满足上述需求,试采用抽象工厂模式对其进行设计。

分析:首先必须搞清楚产品族和产品等级结构。在本例中,游戏操作控制和游戏界面控制是一个产品族,不同操作系统的游戏操作控制是一个产品等级结构,不同操作系统的游戏界面控制是另一个产品等级结构。

设计需求是:软件要求具有较好的扩展性以支持新的操作系统平台。

如果新增IOS操作系统,为了使得这款手游满足需求,只需新增IOS平台操作控制实现操作控制接口,新增IOS平台界面控制实现界面控制接口,同时新增一个IOS工厂实现工厂接口,只是在原代码的基础上扩展了功能,并没有修改原代码,符合开闭原则。

;