简单工厂模式 | 偷掉月亮 (moonshuo.cn)
原文链接:工厂方法模式 | 偷掉月亮 (moonshuo.cn)
在面对简单工厂模式的时候,如果此时我们工厂生产圆形和椭圆形纽扣,那么此时我们可以通过一个简单的工厂进行生产,但是如果此时我们想要进行正方形或者长方形的工厂生产,那么此时我们不得不进行源代码的修改,那么此时就违背了开闭原则,而且当我们再一次需要生产圆形的纽扣,那么还得更改过来。
那么解决方法就是在开一个新的厂子,那么现在我们有圆形纽扣工厂和方形纽扣工厂,那么还是存在一个问题,如果我想要生产⭐型扣子,那么我们还需要建立一个工厂,那么此时我们可以对这个工厂进行抽象为一个父类
工厂方法模式(虚拟构造器模式或者多态构造模式):定义一个用于创建对象的接口,但是让子类决定将哪一个类进行实例化,工厂方法模式让一个类的实例化延迟到了子类
这里表示一个button的类,如果没有共同的代码,可以转换为接口,降低耦合度
package 设计模式.工厂模式;
/**
* @author zss
* @date 2022-07-27 11:39:14
* @description 所有扣子的抽象类
*/
public abstract class Button {
/**表示进行扣子规格的操作 */
public void buttonSpecification(){
System.out.println("正在进行定制中");
};
}
这里时圆形扣子的制作
public class RoundButton extends Button {
@Override
public void buttonSpecification() {
super.buttonSpecification();
System.out.println("这是一个圆形扣子,需要满足圆形的定义");
}
}
这里是方形扣子的制作
package 设计模式.工厂模式;
/**
* @author zss
* @date 2022-07-27 11:46:08
* @description
*/
public class SquareButton extends Button{
@Override
public void buttonSpecification() {
super.buttonSpecification();
System.out.println("这是一个方形扣子,需要满足相应的条件");
}
}
下面是一个抽象的工厂接口类
package 设计模式.工厂模式;
/**
* @author zss
* @date 2022-07-27 11:31:11
* @description 所有工厂的抽象
*/
public interface ButtonFactory {
/**表示每一个子类工厂创建对象的方式*/
public Button factoryMethod();
}
这里是一个圆形扣子的生产方式
package 设计模式.工厂模式;
/**
* @author zss
* @date 2022-07-27 11:33:29
* @description 用于生产圆形扣子的工厂
*/
public class RoundButtonFactory implements ButtonFactory {
@Override
public Button factoryMethod() {
return new RoundButton();
}
}
方形扣子的生产方式
package 设计模式.工厂模式;
/**
* @author zss
* @date 2022-07-27 11:50:05
* @description
*/
public class SquareButtonFactory implements ButtonFactory{
@Override
public Button factoryMethod() {
return new SquareButton();
}
}
客户
package 设计模式.工厂模式;
import 设计模式.简单工厂模式.example1.Factory;
/**
* @author zss
* @date 2022-07-27 11:50:38
* @description 客户
*/
public class Client {
public static void main(String[] args) {
Factory factory;
Button button;
//客户需要圆形扣子,经过一系列的判断,得到下面的例子
Button button1=new RoundButton();
button1.buttonSpecification();
}
}
其实这里的用户应该传输过来一个清单,我们根据清单的内容进行判断,返回创建的对象内容
<demand>
<button>圆形button>
<button>方形button>
<button>星星button>
demand>
package 设计模式.工厂模式;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
/**
* @author zss
* @date 2022-07-25 20:30:01
* @description 此方法用于从xml文件中读取配置
*/
public class XMLUtil {
public static List getFruitType() {
try {
List list=new LinkedList<>();
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dFactory.newDocumentBuilder();
Document doc = (Document) dBuilder.parse(new File("D:\\算法\\src\\设计模式\\工厂模式\\demand.xml"));
NodeList n1 = doc.getElementsByTagName("button");
//在这里如果一次性得到很多的节点,那么使用for循环进行操作,同时工厂中也可以使用for循环
for (int i = 0; i < n1.getLength(); i++) {
Node classNode = n1.item(i).getFirstChild();
String fruitType = classNode.getNodeValue().trim();
list.add(fruitType);
}
return list;
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
return null;
}
}
}
package 设计模式.工厂模式;
import 设计模式.简单工厂模式.example1.Factory;
/**
* @author zss
* @date 2022-07-27 11:50:38
* @description 客户
*/
public class Client {
public static void main(String[] args) {
ButtonFactory buttonFactory;
Button button ;
for (String b : XMLUtil.getFruitType()) {
if (b.equals("圆形")) {
button = new RoundButtonFactory().factoryMethod();
button.buttonSpecification();
} else if (b.equals("方形")) {
button=new SquareButtonFactory().factoryMethod();
button.buttonSpecification();
}else {
System.out.println("我们无法生产这种东西");
}
}
}
}
这样我们可以根据用户的订单大量的生产东西,
上面虽然实现了对用户订单的批量处理,但是我们发现如果现在工厂已经增加了可以生产其他图形的类别,那么我就需要在Client中添加多个代码,那么反而又需要我们进行代码的修改,这样反而违反了开闭原则,我们希望我们这个也可以在xml文件中进行配置。但是可惜的是工厂不像用户清单那样,只需要获得字符串就好,这里需要我们与类进行关联,那么我们就不得不使用到反射
<config>
<buttonType type="圆形" className="设计模式.工厂模式.RoundButtonFactory"/>
<buttonType type="方形" className="设计模式.工厂模式.SquareButtonFactory"/>
config>
package 设计模式.工厂模式;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
/**
* @author zss
* @date 2022-07-28 17:11:40
* @description 读取工厂xml文件的方法,我们需要他返回一个真正的实例
*/
public class XMLFactoryUtil {
public static ButtonFactory getFactory(String buttonType) {
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dFactory.newDocumentBuilder();
Document doc = (Document) dBuilder.parse(new File("D:\\算法\\src\\设计模式\\工厂模式\\classConfig.xml"));
NodeList n1 = doc.getElementsByTagName("buttonType");
String factoryType = null;
for (int i = 0; i < n1.getLength(); i++) {
//得到第一个节点的值,即那个圆形,方形等信息
Node node=n1.item(i);
Element element=(Element) node;
String button = element.getAttribute("type").trim();
if (buttonType.equals(button)){
factoryType=element.getAttribute("className");
break;
}
}
Class> c = Class.forName(factoryType);
Object o = c.newInstance();
return (ButtonFactory) o;
} catch (Exception e) {
throw new RuntimeException("我们无法生产这种图形");
}
}
}
package 设计模式.工厂模式;
/**
* @author zss
* @date 2022-07-27 11:50:38
* @description 客户
*/
public class Client {
public static void main(String[] args) {
ButtonFactory buttonFactory;
Button button ;
for (String b : XMLUtil.getFruitType()) {
buttonFactory=XMLFactoryUtil.getFactory(b);
button=buttonFactory.factoryMethod();
button.buttonSpecification();
}
}
}
是不是很像Spring!!!
当然我们还可以进一步对这个代码进行优化,比如将创建工厂的方法隐藏,因为客户不需要知道你的工厂是怎么创建的,只需要将这个创建的方法移动到创建图形的时候,或者我们也可以提前将这个创建的对象存储到容器中(和spring一样,但是我们可以存储在map中),这些实际的方法都可以进行。
优点:
缺点: