首先我们来一个简单的例子来初步感受下工厂模式:
下面是两个被调用者的类:
class WesternStyle_Food_Chef implements Chef{
public String doFood(){
return "I am a western-style food chef, I like my job.";
}
}
class Chinese_Chef implements Chef{
public String doFood(){
return "我是一名中餐厨师,我享受做菜的每一刻";
}
}
下面这个是调用者的代码:
public class myFactoryModel {
public static void main(String[] args) {
WesternStyle_Food_Chef western_chef = new WesternStyle_Food_Chef();
System.out.println(western_chef.doFood());
Chinese_Chef chinese_chef = new Chinese_Chef();
System.out.println(chinese_chef.doFood());
}
}
下面,我们来仔细分析下上面的代码
我们可以看出调用者在使用被调用者的时候,我们此时指定特定的类名,并指定方法,那么考虑这样一个问题:
如果我们的被调用者程序发生了改变,在此例中,假设不再需要西餐厨师,那个当我删除相关的类的时候,客户端发生错误
而且会引起很多问题,这是调用者和被调用者存在着直接依赖的关系,即为耦合
下面我们使用简单工厂模式进行重构,进行分解,让调用者依赖工厂,而不再依赖于产品,解耦
抽象厨师类为一个接口,并提供抽象方法:
//厨师接口
interface Chef{
public String doFood();
}
下面是工厂类,工厂方法
//工厂类,只负责生产对象
class Chef_Manager{
public static Chef getInstance(String name){
//根据条件生成不同的对象
if("Western".equals(name)){
return new WesternStyle_Food_Chef();
}else if("Chinese".equals(name)){
return new Chinese_Chef();
}
return null;
}
}
我们再看看此时的客户端程序:
public class myFactoryModel {
public static void main(String[] args) {
Chef chinese_chef_factory = Chef_Manager.getInstance("Chinese");
if(chinese_chef_factory != null){
System.out.println(chinese_chef_factory.doFood());
}
Chef western_chef_factory = Chef_Manager.getInstance("Western");
if(western_chef_factory != null){
System.out.println(western_chef_factory.doFood());
}
}
}
此时我们可以看到,我们的客户端程序并不直接使用特定的厨师类型,而是根据需要给出所需的厨师类型,并用其父类接口对象接收返回的对象
我们可以看出工厂,返回一个对象,那么问题来了,很多方法也是直接返回的对象,那我们如何区分方法是不是工厂方法呢?或者说工厂方法有什么特殊之处呢?
其实,工厂方法模式不仅要求有一个能够创建新对象的方法,还要让客户代码无须了解具体实例化的类,工厂方法模式通常包含若干个类,这些类实现了共同的操作并返回相同的抽象类型,而这些却是操作的内部逻辑,实际上却实例化了不同的类,并且这些类都实现了相同的抽象类型,当客户代码请求一个新对象时,这个新对象该由哪个类实例化,取决于工厂对象接收创建请求时的行为。
通常,客户代码使用类的某个构造函数来实例化类,但有些时候,客户代码并不知道使用的是哪个类去创造它所需要的对象,在这种场景下,需要把创建对象的工作转交给一个接口,来完成实例化的控制,此时创建一个标准接口,并创建一个工厂方法来返回该接口的某个实例。我们上面的例子其实就是这么执行的。
当我们有时候碰到并行的层次结构进行建模的时候,我们通常也会选用工厂模式。一个并行层次结构是一对类层次结构,其中一个类在一个层次,与其相关的在另一个层次。当将现有层次结构的部分行为移除该层次后,并行层次结构就会显露出来。
现在我们先总结下:
工厂方法模式的意图是让服务的提供者确定实例化哪个类,而不是客户代码。当你不想让客户代码决定实例化哪个类的时候,就可以使用工厂方法模式。此外,工厂方法模式还可以用于当客户端代码不知道需要创建哪个对象类的时候,例如,客户端不知道当前处于什么样的一个状态。另外,在并行层次结构中使用该模式可以避免类的规模过于庞大。工厂方法模式可以根据一个类层次结构的子类,确定另一个相关层次中哪一个类被实例化,从而建立相对应的并层次结构。
抽象工厂模式:
在创建对象时,有时会知道具体的类去实例化一个对象,可以使用工厂方法模式来定义一个外部方法来决定实例化哪个类。但有时,改变控制实例化的因素有很多,比如,你在中国开一家餐厅,根据当地的人生产食物,某一天,业务扩展到国外,那么我们就要改变我们现在的生产模式了。
此时我们通常会设计一个工具箱,即抽象工厂模式,其意图是允许创建一组相关或相互的依赖对象。
抽象工厂模式使得客户端在需要新的对象的时候,不需要关系该对象是由那个类实例化的,单看这个特点,抽象工厂就像是工厂方法的集合。在一些特定的场合,工厂方法模式的设计可能会转向抽象工厂模式。
使用抽象工厂模式的目的在于为客户端代码做一个准备工作,例如创建一族相关或者相互依赖的对象,即对象组,同时有些对象又不包含在其中,实现需求的定制。
下面我们看一个生产食物的例子:
//抽象糕点类
interface Pastry{
public abstract void getTaste();
public abstract void getComponent();
}
class Dessert implements Pastry{
public void getTaste(){
System.out.println("甜的");
}
public void getComponent(){
System.out.println("牛奶,面粉,水");
}
}
class Bread implements Pastry{
public void getTaste(){
System.out.println("微咸");
}
public void getComponent(){
System.out.println("面粉,荞麦,水");
}
}
//抽象面条类
interface Noodle{
public abstract void getLocal();
}
class Hand_Pulled_Noodle implements Noodle{
public void getLocal(){
System.out.println("中国");
}
}
class Pasta implements Noodle{
public void getLocal(){
System.out.println("意大利");
}
}
下面我们来设计一个抽象工厂,每个工厂可以生产一种面条和甜点
interface AbstractCreator {
public abstract Pastry createPastry();
public abstract Noodle createNoodle();
}
实现特定的工厂
//工厂一生产甜点和意大利面条
class Factory_01 implements AbstractCreator{
public Pastry createPastry() {
return new Dessert();
}
@Override
public Noodle createNoodle() {
return new Pasta();
}
}
//工厂二生产面包和拉面
class Factory_02 implements AbstractCreator{
public Pastry createPastry() {
return new Bread();
}
public Noodle createNoodle() {
return new Hand_Pulled_Noodle();
}
}
下面我们实现一个客户端类,来使用工厂
public class Client {
public static void main(String[] args) {
//创建两个工厂
AbstractCreator factory_01 = new Factory_01();
AbstractCreator factory_02 = new Factory_02();
//用工厂生产甜点
Pastry pastry_create01 = factory_01.createPastry();
Pastry pastry_create02 = factory_02.createPastry();
pastry_create01.getTaste();
pastry_create01.getComponent();
pastry_create02.getTaste();
pastry_create02.getComponent();
//用工厂生产面条
Noodle noodle_create01 = factory_02.createNoodle();
Noodle noodle_create02 = factory_02.createNoodle();
noodle_create01.getLocal();
noodle_create02.getLocal();
}
}
从上面的例子我们可以看到,抽象工厂设计了工厂产生的产品,而具体的工厂根据不同的需求实现自己的业务逻辑,每个工厂产生一族对象,而当我们的被调用者对一旦发生改变,由于被调用者的实现对调用者来说是透明的,因此我们客户端根本就不用关心被调用者的变化,客户端仍然是正常工作的,我们只有去根据需求定制工厂就好了。