原文链接:简单工厂模式 | 偷掉月亮 (moonshuo.cn)
概述
定义一个工厂类,它可以根据参数的不同的返回不同类的实例,被创建的实例具有共同的父类。
代码举例
比如我们要求工厂提供水果,我们不需要关心工厂的水果如何创建出来,只是关心我们给出水果的名称,工厂能否得到对应的水果。
package 设计模式.简单工厂模式.example1;
/**
* @author 21050
* @date 2022-07-25 19:59:06
* @description 所有水果的父类
*/
public abstract class Fruit {
protected String fruitName;
/**水果的生长方式不同*/
public abstract void growingFruit();
/**假设水果的运输方式相同*/
public void transportation(){
System.out.println("开始运输"+fruitName+"到工厂");
}
public Fruit(String fruitName) {
this.fruitName = fruitName;
}
}
工厂
package 设计模式.简单工厂模式.example1;
/**
* @author 21050
* @date 2022-07-25 19:57:58
* @description 简单工厂模式的工厂
*/
public class Factory {
//静态工厂方法
public static Fruit getFruit(String name) {
Fruit fruit = null;
if (name.equals("苹果")) {
fruit = new AppleFruit(name);
} else if (name.equals("西瓜")) {
fruit=new MelonFruit(name);
}else {
System.out.println("没有这种水果");
}
return fruit;
}
}
实现类
/**
* @author 21050
* @date 2022-07-25 20:05:57
* @description
*/
public class AppleFruit extends Fruit{
@Override
public void growingFruit() {
System.out.println("我是水果"+fruitName+"长在树上,需要工人来树上采摘我");
}
public AppleFruit(String fruitName) {
super(fruitName);
}
}
/**
* @author 21050
* @date 2022-07-25 20:07:57
* @description
*/
public class MelonFruit extends Fruit{
public MelonFruit(String fruitName) {
super(fruitName);
}
@Override
public void growingFruit() {
System.out.println("我是水果"+fruitName+"长在地上,需要工人们来地上采摘我");
}
}
客户
/**
* @author 21050
* @date 2022-07-25 20:12:26
* @description
*/
public class Client {
public static void main(String[] args) {
Fruit fruit=null;
fruit=Factory.getFruit("苹果");
System.out.println("我想知道这个水果怎么生长的");
fruit.growingFruit();
fruit.transportation();
}
}
优化
此时我们还可以发现这个时候仍旧当我们需要更改水果的时候,此时仍旧需要在main方法中读取这个信息,如果此时客户给我们一个单子,上面列满的水果,那么我们就需要重新传输很多参数,同时也不符合开闭原则,如果能将这个写入到xml文件中,那就好了
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<fruitType>苹果</fruitType>
</config>
package 设计模式.简单工厂模式.example1;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import java.io.*;
/**
* @author 21050
* @date 2022-07-25 20:30:01
* @description 此方法用于从xml文件中读取配置
*/
public class XMLUtil {
public static String getFruitType() {
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dFactory.newDocumentBuilder();
Document doc = (Document) dBuilder.parse(new File("D:\\算法\\src\\设计模式\\简单工厂模式\\example1\\FruitList.xml"));
NodeList n1 = doc.getElementsByTagName("fruitType");
//在这里如果一次性得到很多的节点,那么使用for循环进行操作,同时工厂中也可以使用for循环
Node classNode = n1.item(0).getFirstChild();
String fruitType = classNode.getNodeValue().trim();
return fruitType;
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
return null;
}
}
}
创建对象和使用对象
在开发中建议将创建对对象和使用对象分开,降低程序之间的耦合,如果创建对象与使用对象在一个类中共同使用
class LoginAction {
private UserDAO udao;
public LoginAction() {
udao = new JDBCUserDAO(); //创建对象
}
public void execute() {
//其他代码
udao.findUserById(); //使用对象
//其他代码
}
}
假如说下面的类使用JDBCUserDAO()这个子类的方法,那么现在我需在这个类中使用另外一个子类的方法,那么现在就需要更改构造器,这样改变源码,不符合开闭原则。
同时我们也需要注意到,并不是每一次创建对象都需要我们开启工厂模式,比如String类,我们直接调用构造方法即可,而不是要根据特定的字符串创建不同的字符串类,如果真的这样的话,那么Java中可能要包含所有语言的所有组合的字符串了。
优缺点
简单工厂模式的主要优点如下:
(1) 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
(2) 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
(3) 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点:
(1) 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
(2) 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
(3) 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
(4) 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
适用场景:
(1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
(2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。