工厂方法模式

简单工厂模式 | 偷掉月亮 (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();
    }
}

优化1

其实这里的用户应该传输过来一个清单,我们根据清单的内容进行判断,返回创建的对象内容

<?xml version="1.0" encoding="UTF-8" ?>
<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<String> getFruitType() {
        try {
            List<String> 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("我们无法生产这种东西");
            }
        }
    }
}

image-20220727121433269

这样我们可以根据用户的订单大量的生产东西,

优化2

上面虽然实现了对用户订单的批量处理,但是我们发现如果现在工厂已经增加了可以生产其他图形的类别,那么我就需要在Client中添加多个代码,那么反而又需要我们进行代码的修改,这样反而违反了开闭原则,我们希望我们这个也可以在xml文件中进行配置。但是可惜的是工厂不像用户清单那样,只需要获得字符串就好,这里需要我们与类进行关联,那么我们就不得不使用到反射

<?xml version="1.0" encoding="UTF-8" ?>
<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();
        }
    }
}

image-20220728180917344

是不是很像Spring!!!

总结

当然我们还可以进一步对这个代码进行优化,比如将创建工厂的方法隐藏,因为客户不需要知道你的工厂是怎么创建的,只需要将这个创建的方法移动到创建图形的时候,或者我们也可以提前将这个创建的对象存储到容器中(和spring一样,但是我们可以存储在map中),这些实际的方法都可以进行。

优点:

  1. 扩展性高,新的产品来的时候,我们可以直接添加产品细节和工厂建造
  2. 完全对外部隐藏了创建的细节,客户不知道工厂怎么创建的,但是知道有这个工厂,而且客户的产品也可以准确生产,而且客户也不需要知道生产过程

缺点:

  1. 引入抽象层,可理解性变差
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值