该模式用来封装和管理类的创建,终极目的是为了解耦,实现创建者和调用者的分离。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
工厂模式可以细分为三种:
简单工厂模式
工厂方法模式
抽象工厂模式
参考文章:(218条消息) 工厂模式超详解!_王哈哈哈.的博客-CSDN博客_工厂模式
视频讲解:五分钟学设计模式.02.简单工厂模式_哔哩哔哩_bilibili
本文加入了自己的理解与其他文章里的资料。
工厂模式的本质就是对获取对象过程的抽象。
在介绍工厂模式之前先来看看传统模式。
以点披萨的为例,简单的点披萨代码如下:
Pizza orderPizza() {
Pizza pizza = new Pizza();
//准备材料
pizza.prepare();
//烘烤
pizza.bake();
//切
pizza.cut();
//装盒
pizza.box();
return pizza;
}
但是假如我们需要点不同种类的披萨,那我们可以直接在上述代码中添加根据披萨的种类生成不同类型的对象:
Pizza orderPizza(String type) {
Pizza pizza;
if(type.equals("chess"){
pizza = new ChessPizza();
}else if(type.equals("greek"){
pizza = new GreekPizza();
}else if(type.equals("apple"){
pizza = new ApplePizza();
}
....
//准备材料
pizza.prepare();
//烘烤
pizza.bake();
//切
pizza.cut();
//装盒
pizza.box();
return pizza;
}
(注:上述代码中就使用了多态的思想)
此时类图如下,此时让调用者直接去创建披萨的对象。
在传统模式中直接让调用者去创建对象。但是,假如创建对象的“create”过程比较复杂,我们可以将其交由给一个新的类来创建,这种类叫工厂类,调用者只需要调用这个类里的函数来创建对象就行了,无需自己书写创建对象的函数。
Factroy要解决的问题是:希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节。
(请特别留意“创建过程比较复杂“这个条件。如果不复杂,用构造函数就够了。有关于“创建过程复杂”的例子可以参考这篇文章中,大宽宽的回答:(45 封私信 / 88 条消息) 工厂模式(factory Method)的本质是什么?为什么引入工厂模式? - 知乎 (zhihu.com))
运用到实际例子中,我们将在原来的orderPizza中的代码放到工厂类里来实现:
public class SimpleFactory {
public static Pizza createPizza(String pizzaType){
Pizza pizza = null;
System.out.println("使用了简单工厂模式");
if (pizzaType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("greek");
} else if (pizzaType.equals("chess")) {
pizza = new ChessPizza();
pizza.setName("chess");
} else if (pizzaType.equals("pepper")) {//新增PepperPizza的时候 修改了源代码 违反了ocp原则 如果新增10000个?
//那就很麻烦了
pizza = new PepperPizza();
pizza.setName("pepper");
}
return pizza;
}
}
此时简单工厂的类图如下:
对比传统模式和简单工厂模式的类图可以看出来,在OrderPizza和Pizza中间又加了一层,原来是OrderPizza依赖Pizza,后来让SampleFactory依赖Pizza。
通过封装SimpleFactory这个类,我们将OrderPizza和Pizza进行了解耦合。
不过除了创建对象是在工厂里进行,其他工作还是在orderPizza里进行,此处补充有了工厂类后,OrderPizza类应该修改和增添的代码:
public class OrderPizza {
public OrderPizza(SimpleFactory factory){
setFactory(factory);
}
SimpleFactory factory = null;
public void setFactory(SimpleFactory simpleFactory){
String orderType = "";//用户输入的
this.factory = simpleFactory;
do{
orderType = getType();
Pizza pizza = factory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订购Pizza失败!");
break;
}
}while (true);
}
//写一个方法 可以获取客户希望订购的pizza
private String getType() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请在下面输入你要购买的Pizza");
String pizza = reader.readLine();
return pizza;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
当我们产品增多时,有很多种产品,那么如果依然使用简单工厂模式,那么这个工厂就需要完成所有类型产品的创建,这意味着它需要知道不同类型产品的细节。
这存在一个问题,当我们有新的产品需要增加时,就需要直接在工厂类里去添加新的代码,而这违背了OCP原则,即开闭原则Open-Closed Principle,
对扩展开放,对修改关闭。
于是此处,我们让工厂这个类抽象化,让具体的子类去执行实例化对象的操作,类图如下:
抽象工厂:
定义一个抽象的接口,让子类决定实例化哪个类。
public abstract class AbstractFactory {
//具体实现在子类,应用到了多态的特性
abstract Pizza createPizza(String orderType);
}
具体工厂:
public class BJFactory extends AbstractFactory {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("chess")){
pizza = new BJChessPizza();
}else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
}
return pizza;
}
}
public class LDFactory extends AbstractFactory {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("chess")){
pizza = new LDChessPizza();
}else if (orderType.equals("pepper")){
pizza = new LDPepperPizza();
}
return pizza;
}
}
工厂方法将对象的实例化推迟到了子类,怎么理解呢?
之前的SimpleFactory在createPizza中直接就new出来了,但是在工厂方法中,我们将createPizza这个动作推迟到了AbstractFactory的子类(XXFactory)中才完成.
原来是由一个对象(SimpleFactory)负责所有具体类的实例化,现在实例化Pizza的责任被移到一个抽象类的子类中,此方法就如同一个工厂,所以我个人更喜欢称它为方法工厂。
好处:简单工厂模式OrderPizza依赖SimpleFactory,工厂方法模式OrderPizza依赖AbstractFactory,所以我们更倾向于后者,我们编程时要尽可能的依赖抽象,这样的话后期的拓展可维护性就会很好,比如我们后期要拓展一个莫斯科披萨,我们就直接编写莫斯科披萨类就好,让他继承AbstractFactory类,实现他自己的功能,这样完全不用修改原来的代码,很好的遵守了OCP原则。但是我们如果还是继续使用简单工厂模式的话,要加入莫斯科chess披萨,我们就要修改SimpleFactory,违反了OCP原则,也没有遵守依赖倒转原则。
补充:
(219条消息) 六大原则之依赖倒转(倒置)原则_老街灬的博客-CSDN博客_依赖倒转原则
依赖倒转(倒置)的中心思想是面向接口编程
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
抽象工厂模式可以使得具体工厂类可以创建多个大类(不同产品)的对象,不过还是需要修改抽象工厂和具体工厂的代码,违反开闭原则。
假如这个做披萨的工厂想拓展业务,不仅仅是做不同种类的披萨了,而是转而做其他类型的产品,那此时我们要怎么修改原有的代码呢?
很简单,只需要在抽象工厂类中新增创建该产品的抽象方法,然后在具体工厂子类中实现它即可。
public interface AbstractFactory {
Pizza createPizza(String orderType);
···········
Sauce createSauce(String orderType);
········
//创建蔬菜的方法
Vegetable createVegetable(String orderType);
········
}
public class BJFactory implements AbstractFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~~~使用的是抽象工厂模式~~~");
Pizza pizza = null;
if (orderType.equals("chess")){
pizza = new BJChessPizza();
}else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
}
return pizza;
}
//实现其他产品的方法
public Vegetable createVegetable (String orderType) {
System.out.println("~~~使用的是抽象工厂模式~~~");
Vegetable vegetable = null;
if (orderType.equals("Shengcai")){
Vegetable = new BJShengcaiVegetable ();
}else if (orderType.equals("Youmaicai")){
Vegetable = new BJYoumaicaiVegetable();
}
return Vegetable ;
}
}
可以看到,抽象工厂仅仅是在工厂方法模式下新增了一些接口,只是工厂模式的一个拓展,当抽象工厂模式只有一个产品体系的话就会退化成工厂模式。
所以这两者其实没有什么太大的区别。
不过,假如我们此处新增一个产品体系,我们还是得去修改原有的工厂逻辑,包括抽象工厂以及所有具体工厂。还是违反了开闭原则。
设计模式具体还是一种指导思想,怎么运用还是取决于具体的实例。