java设计模式-工厂模式(上)

什么是工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

工厂模式可以分为三类:简单工厂模式(静态工厂模式)、工厂方法模式 和 抽象工厂模式。

其中简单工厂模式,严格上来说,不属于23设计模式之一,因为它违背了开闭原则,更好的是看作为一种编程方式。

本篇中会介绍三种工厂的使用,也就是

  • 简单(静态)工厂模式(不属于GOF的23种经典设计模式)
  • 工厂方法模式
  • 抽象工厂模式

使用场景

日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。

等等。。。

下面我们模拟手机工厂生产手机。。。

核心角色

  • 抽象产品:定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口,规定了产品对象的共同方法。
  • 具体产品:实现了抽象产品接口,定义了具体产品的特定行为和属性。
  • 抽象工厂:声明了创建产品的抽象方法,可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。
  • 具体工厂:实现了抽象工厂接口,负责实际创建具体产品的对象。

1.简单(静态)工厂模式

实现了抽象工厂接口,负责实际创建具体产品的对象。

结构

简单工厂包含如下角色:

  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现或者继承抽象产品的子类
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品。

示例

模拟手机工厂生产手机。

我们需要定义一个抽象手机产品,定义手机产品的品牌和颜色两个方法

java 复制代码
/**
 * 手机产品抽象类
 *
 * @author jiangkd
 * @date 2023/7/28 14:03:15
 */
public abstract class AbstractPhone {

    /**
     * 手机品牌
     */
    public abstract void brand();

    /**
     * 手机颜色
     */
    public abstract void color();

}

然后有两个具体产品,一加和真我,哈哈哈

java 复制代码
/**
 * 具体手机产品, 一加手机
 *
 * @author jiangkd
 * @date 2023/7/28 14:06:17
 */
@Slf4j
public class YijiaPhone extends AbstractPhone {

    @Override
    public void brand() {
        log.info("品牌, 一加手机!!!");
    }

    @Override
    public void color() {
        log.info("黑色!!!");
    }
    
}
java 复制代码
/**
 * 具体手机产品, 真我手机
 *
 * @author jiangkd
 * @date 2023/7/28 14:07:27
 */
@Slf4j
public class ZhenwoPhobe extends AbstractPhone {

    @Override
    public void brand() {
        log.info("品牌, 真我手机!!!");
    }

    @Override
    public void color() {
        log.info("白色!!!");
    }

}

产品有了,现在我们创建一个手机工厂,可以根据不同参数创建不同品牌手机

java 复制代码
/**
 * 手机产品的工厂
 *
 * @author jiangkd
 * @date 2023/7/28 14:09:50
 */
public class PhoneFactory {

    /**
     * 具体如何产生一个产品的对象,是由具体的工厂类实现的
     * <p>
     * 注意, 这里是静态方法
     *
     * @param clazz
     * @return AbstractPhone
     * @throws Exception e
     */
    public static AbstractPhone createPhone(Class clazz) throws Exception {

        // 全限定类名
        final String name = clazz.getName();

        // 根据反射, 创建具体产品对象
        return (AbstractPhone) Class.forName(name).newInstance();

    }

    /**
     * 当前方法其实和createPhone方法是一样的作用, 两个方法用哪个都可以
     * <p>
     * 注意, 这里是静态方法
     *
     * @param brand 根据参数区分创建哪个具体产品
     * @return AbstractPhone
     */
    public static AbstractPhone createPhone2(String brand) throws Exception {

        if (CharSequenceUtil.equals("yijia", brand)) {
            return new YijiaPhone();
        } else if (CharSequenceUtil.equals("zhenwo", brand)) {
            return new ZhenwoPhobe();
        } else {
            throw new Exception("参数异常!!!");
        }

    }

}

测试一下

java 复制代码
/**
 * @author jiangkd
 * @date 2023/7/28 14:17:25
 */
@Slf4j
public class DemoTest {

    public static void main(String[] args) throws Exception {

        // 创建一加手机
        AbstractPhone yijiaPhone = PhoneFactory.createPhone(YijiaPhone.class);
        yijiaPhone.brand();
        yijiaPhone.color();

        log.info("--------------------------------");

        yijiaPhone = PhoneFactory.createPhone2("yijia");
        yijiaPhone.brand();
        yijiaPhone.color();

        log.info("--------------------------------");

        // 创建真我手机
        AbstractPhone zhenwoPhone = PhoneFactory.createPhone(ZhenwoPhobe.class);
        zhenwoPhone.brand();
        zhenwoPhone.color();

        log.info("--------------------------------");

        zhenwoPhone = PhoneFactory.createPhone2("zhenwo");
        zhenwoPhone.brand();
        zhenwoPhone.color();

    }

}

运行结果如下:

14:19:59.846 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.YijiaPhone - 品牌, 一加手机!!!

14:19:59.851 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.YijiaPhone - 黑色!!!

14:19:59.851 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.DemoTest - --------------------------------

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.YijiaPhone - 品牌, 一加手机!!!

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.YijiaPhone - 黑色!!!

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.DemoTest - --------------------------------

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.ZhenwoPhobe - 品牌, 真我手机!!!

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.ZhenwoPhobe - 白色!!!

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.DemoTest - --------------------------------

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.ZhenwoPhobe - 品牌, 真我手机!!!

14:19:59.882 [main] INFO demo.basic.example.design_patterns.gcms.jdgcms.ZhenwoPhobe - 白色!!!

其实可以看出来,这里我们只有两个具体的手机产品,简答(静态)工厂模式还是很方便的,但你有没有发现,如果现在我陆陆续续追加其他具体产品 红米!努比亚!荣耀!那么我就要陆陆续续三次修改手机工厂类(追加具体产品不算修改,是扩展功能)的功能,这显然不符合java设计模式的六大原则-开闭原则

那不如我们把工厂拆开,每个工厂只生产一种具体的手机产品,这样如果我需要增加一个红米,就只需要添加一个红米工厂和一个红米产品类就可以了,这就是接下来要演示的工厂方法模式

2.工厂方法模式

简单工厂模式确实很方便,但是每次新增一个手机产品都需要改动整个工厂的原有代码,现在使用工厂方法模式改造一下。

抽象手机产品AbstractPhone 和 两个具体的手机产品(一加,真我)不变,依然如上。

工厂类PhoneFactory不要了,我们需要重新创建工厂类。

现在我们首先创建一个抽象工厂类,用来生产AbstractPhone

java 复制代码
/**
 * 定义产品的抽象工厂类
 *
 * @author jiangkd
 * @date 2023/7/28 14:43:04
 */
public abstract class AbstractPhoneFactory {

    /**
     * 构建产品, 具体实现交给子类
     *
     * @return AbstractPhone
     */
    abstract AbstractPhone createPhone();

}

然后分别是一加手机和真我手机的两个具体工厂实现类,每个实现类针对一个具体手机品牌的生产

java 复制代码
/**
 * 具体产品一加手机的工厂实现类
 *
 * @author jiangkd
 * @date 2023/7/28 14:44:53
 */
public class YijiaPhoneFactory extends AbstractPhoneFactory {

    @Override
    AbstractPhone createPhone() {
        return new YijiaPhone();
    }

}
java 复制代码
/**
 * 具体产品真我手机的工厂实现类
 *
 * @author jiangkd
 * @date 2023/7/28 14:45:35
 */
public class ZhenwoPhoneFactory extends AbstractPhoneFactory {

    @Override
    AbstractPhone createPhone() {
        return new ZhenwoPhobe();
    }

}

测试一下

java 复制代码
/**
 * @author jiangkd
 * @date 2023/7/28 14:46:16
 */
@Slf4j
public class DemoTest {

    public static void main(String[] args) {

        // 生产一加手机
        final AbstractPhoneFactory yijiaPhoneFactory = new YijiaPhoneFactory();
        final AbstractPhone yijiaPhone = yijiaPhoneFactory.createPhone();

        yijiaPhone.brand();
        yijiaPhone.color();

        log.info("--------------------------------");

        // 生产真我手机
        final AbstractPhoneFactory zhenwoPhoneFactory = new ZhenwoPhoneFactory();
        final AbstractPhone zhenwoPhone = zhenwoPhoneFactory.createPhone();

        zhenwoPhone.brand();
        zhenwoPhone.color();

    }

}

运行结果如下:

14:48:22.596 [main] INFO demo.basic.example.design_patterns.gcms.YijiaPhone - 品牌, 一加手机!!!

14:48:22.598 [main] INFO demo.basic.example.design_patterns.gcms.YijiaPhone - 黑色!!!

14:48:22.598 [main] INFO demo.basic.example.design_patterns.gcms.gcffms.DemoTest - --------------------------------

14:48:22.599 [main] INFO demo.basic.example.design_patterns.gcms.ZhenwoPhobe - 品牌, 真我手机!!!

14:48:22.599 [main] INFO demo.basic.example.design_patterns.gcms.ZhenwoPhobe - 白色!!!

现在我们如果想要加一个红米手机,只需要创建具体红米产品和对应的工厂类就可以了

java 复制代码
/**
 * 具体手机产品 红米
 *
 * @author jiangkd
 * @date 2023/7/28 14:51:09
 */
@Slf4j
public class HongmiPhone extends AbstractPhone {

    @Override
    public void brand() {
        log.info("品牌, 红米手机!!!");
    }

    @Override
    public void color() {
        log.info("红色!!!");
    }
    
}
java 复制代码
/**
 * 红米手机工厂实现类
 *
 * @author jiangkd
 * @date 2023/7/28 14:52:04
 */
public class HongmiPhoneFactory extends AbstractPhoneFactory {

    @Override
    AbstractPhone createPhone() {
        return new HongmiPhone();
    }

}

测试

java 复制代码
/**
 * @author jiangkd
 * @date 2023/7/28 14:46:16
 */
@Slf4j
public class DemoTest {

    public static void main(String[] args) {

        // 生产一加手机
        final AbstractPhoneFactory yijiaPhoneFactory = new YijiaPhoneFactory();
        final AbstractPhone yijiaPhone = yijiaPhoneFactory.createPhone();

        yijiaPhone.brand();
        yijiaPhone.color();

        log.info("--------------------------------");

        // 生产真我手机
        final AbstractPhoneFactory zhenwoPhoneFactory = new ZhenwoPhoneFactory();
        final AbstractPhone zhenwoPhone = zhenwoPhoneFactory.createPhone();

        zhenwoPhone.brand();
        zhenwoPhone.color();

        log.info("--------------------------------");

        // 生产红米手机
        final AbstractPhoneFactory hongmiPhoneFactory = new HongmiPhoneFactory();
        final AbstractPhone hongmiPhone = hongmiPhoneFactory.createPhone();

        hongmiPhone.brand();
        hongmiPhone.color();

    }

}

执行结果

14:53:43.272 [main] INFO demo.basic.example.design_patterns.gcms.YijiaPhone - 品牌, 一加手机!!!

14:53:43.274 [main] INFO demo.basic.example.design_patterns.gcms.YijiaPhone - 黑色!!!

14:53:43.274 [main] INFO demo.basic.example.design_patterns.gcms.gcffms.DemoTest - --------------------------------

14:53:43.276 [main] INFO demo.basic.example.design_patterns.gcms.ZhenwoPhobe - 品牌, 真我手机!!!

14:53:43.276 [main] INFO demo.basic.example.design_patterns.gcms.ZhenwoPhobe - 白色!!!

14:53:43.276 [main] INFO demo.basic.example.design_patterns.gcms.gcffms.DemoTest - --------------------------------

14:53:43.276 [main] INFO demo.basic.example.design_patterns.gcms.HongmiPhone - 品牌, 红米手机!!!

14:53:43.276 [main] INFO demo.basic.example.design_patterns.gcms.HongmiPhone - 红色!!!

符合开闭原则,不修改原来的功能,只是添加产品和工厂类就可以了。

细心的小伙伴又会问了,虽然不用在原基础功能上修改,但是如果还要添加10个品牌手机,岂不是要加10个产品类和10个工厂类,增加维护难度呀!是的,这就是工厂方法模式的弊端,增加系统的复杂度。

所以你可以看出,简单(静态)工厂和工厂方法模式,都是针对一类产品的生产,也就是这些工厂只生产同种类产品,同种类产品称为同等级产品。(工厂方法模式只考虑生产同等级的产品)

但实际上呢,现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

这就是下面的抽象工厂模式了。。。,我们继续延申,现在老板发话了,生产手机的时候,顺便把耳机也生产出来吧

3.抽象工厂模式

由于篇幅问题,继续查看java设计模式-工厂模式(下)

相关推荐
卡尔特斯2 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin
白鲸开源2 小时前
Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录
java·ubuntu·开源
ytadpole3 小时前
Java 25 新特性 更简洁、更高效、更现代
java·后端
纪莫3 小时前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide4 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端
用户3721574261354 小时前
Java 轻松批量替换 Word 文档文字内容
java
白鲸开源4 小时前
教你数分钟内创建并运行一个 DolphinScheduler Workflow!
java
晨米酱4 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
Java中文社群5 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心5 小时前
从零开始学Flink:数据源
java·大数据·后端·flink