• 简单工厂模式


    原文链接:简单工厂模式 | 偷掉月亮 (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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    工厂

    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;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    实现类

    /**
     * @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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    /**
     * @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+"长在地上,需要工人们来地上采摘我");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    客户

    /**
     * @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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20220725202012861

    优化

    此时我们还可以发现这个时候仍旧当我们需要更改水果的时候,此时仍旧需要在main方法中读取这个信息,如果此时客户给我们一个单子,上面列满的水果,那么我们就需要重新传输很多参数,同时也不符合开闭原则,如果能将这个写入到xml文件中,那就好了

    
    
        苹果
    
    
    • 1
    • 2
    • 3
    • 4
    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;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    创建对象和使用对象

    在开发中建议将创建对对象和使用对象分开,降低程序之间的耦合,如果创建对象与使用对象在一个类中共同使用

    class LoginAction {
    	private UserDAO udao;
    	
    	public LoginAction() {
    		udao = new JDBCUserDAO(); //创建对象
    	}
    	
    	public void execute() {
    		//其他代码
    		udao.findUserById(); //使用对象
    		//其他代码
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    假如说下面的类使用JDBCUserDAO()这个子类的方法,那么现在我需在这个类中使用另外一个子类的方法,那么现在就需要更改构造器,这样改变源码,不符合开闭原则。

    同时我们也需要注意到,并不是每一次创建对象都需要我们开启工厂模式,比如String类,我们直接调用构造方法即可,而不是要根据特定的字符串创建不同的字符串类,如果真的这样的话,那么Java中可能要包含所有语言的所有组合的字符串了。

    优缺点

    简单工厂模式的主要优点如下:

       (1) 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
    
       (2) 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
    
       (3) 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    缺点:

    (1) 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。

       (2) 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
    
       (3) 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
    
       (4) 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    适用场景:

    (1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

    (2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

  • 相关阅读:
    C 基础语法3 —— 指针
    解决方案 | 如何构建市政综合管廊安全运行监测系统?
    springboot监控
    如何利用CSS实现三角形、扇形、聊天气泡框
    如何获取JDK Proxy动态代理生成的代理类源代码
    【2022年11月22日提高A组】数论计算【SPFA】【数学】
    Unity 2D 游戏学习笔记(1)
    计算机网络期末复习-Part1
    [Qt][C++]static与extern关键字
    【Final Project】Kitti的双目视觉里程计(1)
  • 原文地址:https://blog.csdn.net/qq_58510930/article/details/126122972