设计模式之工厂方法模式

1.工厂方法模式概述

​ 工厂方法模式(Pattern:Factory Method)属于创建型模式,其意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,将核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

2.工厂方法模式的角色及其职责及使用步骤

在这里插入图片描述
抽象工厂[Creator]:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
具体工厂角色[Concrete Creator]:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。
抽象产品角色[Product]:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品角色[Concrete Product]:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

组成(角色)关系作用
抽象产品(Product)具体产品的父类描述具体产品的公共接口
具体产品(Concrete Product)抽象产品的子类;工厂类创建的目标类描述生产的具体产品
抽象工厂(Creator)具体工厂的父类描述具体工厂的公共接口
具体工厂(Concrete Creator)抽象工厂的子类;被外界调用描述具体工厂;实现FactoryMethod工厂方法创建产品的实例

3.实例解析

背景:有一个游戏角色工厂(仅生产Hoyi角色);用户则要求再生产Yase和Luban两类角色。
冲突:改变原有游戏角色工厂的配置和变化非常困难,假设下一次客户需要再发生变化,再次改变将增大非常大的成本。
解决方案:新办角色工厂Yase和Luban分别生产Yase和Luban两类角色。
在这里插入图片描述

步骤1 创建抽象类工厂,定义具体工厂的公共接口
public interface RoleFactory {
    public Role produceRole();
}
步骤2 创建抽象类产品,定义具体产品的公共接口
public interface Role {
    public void play();
}
步骤3 创建具体产品类(继承抽象产品类), 定义生产的具体产品
public class HouyiRole implements Role {
    public void play(){
    	System.out.println("后羿初始化中......");
    }
}

public class LubanRole implements Role {
    public void play(){
    	System.out.println("鲁班初始化中......");
    }
}

public class YaseRole implements Role {
    public void play(){
    	System.out.println("亚瑟初始化中......");
    }
}
步骤4 创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法
public class HouyiRoleFactory implements RoleFactory {
    public Role produceRole(){
    	System.out.println("后羿工厂生产后羿角色......");
        return new HouyiRole();
    }
}

public class LubanRoleFactory implements RoleFactory {
    public Role produceRole(){
    	System.out.println("鲁班工厂生产鲁班角色......");
        return new LubanRole();
    }
}

public class YaseRoleFactory implements RoleFactory {
    public Role produceRole(){
    	System.out.println("亚瑟工厂生产亚瑟角色......");
        return new YaseRole();
    }
}
步骤5 配置文件

​ 在实际的应用开发中,一般将具体工厂类的实例化过程进行改进,不直接使用new关键字来创建对象,而是将具体类的类名写入配置文件中,再通过Java的反射机制,读取XML格式的配置文件,根据存储在XML文件中的类名字符串生成对象。

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<className>HouyiRoleFactory</className>
</config>
步骤6 工具类XMLUtil

Java反射(Java Reflection):

​ 是指在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、超类等信息,还包括实例的创建和实例类型的判断等。可通过Class类的forName()方法返回与带有给定字符串名的类或接口相关联的Class对象,再通过newInstance()方法创建此对象所表示的类的一个新实例,即通过一个类名字符串得到类的实例

//创建一个字符串类型的对象
 Class c = Class.forName(“String”);
 Object obj = c.newInstance();
 return obj;
XMLUtil代码
import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLUtil {
	public static Object getBean() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException {
		try {
			//创建文档对象
			DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dFactory.newDocumentBuilder();
			Document doc ;
			doc = builder.parse(new File("config.xml"));
			
			//获取包含类名的文本节点
			NodeList nl = doc.getElementsByTagName("className");
			Node classNode=nl.item(0).getFirstChild();
			String cName=classNode.getNodeValue();

			//通过类名生成实例对象并将其返回
			Class c=Class.forName(cName);
			Object obj=c.newInstance();
			return obj;

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			return null;
		}
	}
}
步骤7 客户端测试
import java.io.IOException;
import org.xml.sax.SAXException;

public class Client {
	public static void main(String[] args) {
		try {
			Role role;
			RoleFactory factory;
			factory=(RoleFactory)XMLUtil.getBean();
			role=factory.produceRole();
			role.play();
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SAXException
				| IOException e) {
			e.printStackTrace();
		}
	}
}

运行结果:
在这里插入图片描述

4.工厂方法模式优缺点

优点
​ • 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。

​ • 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。

​ • 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。

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

​ • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

​ • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;

​ • 一个具体工厂只能创建一种具体产品

5.应用场景

• 当需要一个对象时,我们不需要知道该对象所对应的具体类,只要知道哪个具体工厂可以生成该对象,实例化这个具体工厂即可创建该对象。

• 类的数目不固定,随时有新的子类增加进来,或者是还不知道将来需要实例化哪些具体类。

• 定义一个创建对象接口,由子类决定要实例化的类是哪一个;客户端可以动态地指定工厂子类创建具体产品。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值