java基础--工厂模式解析

本文深入解析工厂模式,包括简单工厂、工厂方法和抽象工厂的概念、应用与优缺点,通过实例展示如何在代码中实现这些模式,帮助读者理解设计模式在软件工程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工厂模式作为常见且非常重要的设计模式(其实设计模式都挺重要的),值得多次回顾与深入理解,以下为我自己对工厂模式的一些理解及分析


工厂模式整体分为简单工厂,工厂方法,抽象工厂等,但简单工厂其实不能算是设计模式,而是一种编码习惯,但很多情况下会将简单工厂误认为是工厂模式的一种,这里也一起涵盖,接下来以电脑组装为例分别分析

首先是简单工厂,简单工厂其实是一种编码习惯,上文已经说过,简单工厂的做法是将创建对象的代码单独拆分开成为一部分,单独维护,来看一个例子

// 电脑品牌枚举
public enum  ComputerEnum {
    MSI,Lenovo,Hp
}

// 电脑接口,定义一台电脑的组成部分
public interface  Computer {

    //获取CPU
    void CPU();

    //获取主板
    void Motherboard();

    //获取散热器
    void Radiator();

    //获取内存条
    void RAM();

    //获取显卡
    void GPU();

    //获取硬盘
    void HDD();

    //获取电源
    void PowerSupply();

    //获取机箱
    void MachineBox();

    //组装电脑
    void Assemble();
}


// 微星
public class MsiComputer implements Computer {

    @Override
    public void CPU() {
        System.out.println("CPU---MSI");
    }

    @Override
    public void Motherboard() {
        System.out.println("主板---MSI");
    }

    @Override
    public void Radiator() {
        System.out.println("散热器---MSI");
    }

    @Override
    public void RAM() {
        System.out.println("内存---MSI");
    }

    @Override
    public void GPU() {
        System.out.println("GPU---MSI");
    }

    @Override
    public void HDD() {
        System.out.println("HDD---MSI");
    }

    @Override
    public void PowerSupply() {
        System.out.println("电源---MSI");
    }

    @Override
    public void MachineBox() {
        System.out.println("机箱---MSI");
    }

    @Override
    public void Assemble() {
        System.out.println("组装完成---MSI");
    }
}


// 惠普
public class HpComputer implements Computer {

    @Override
    public void CPU() {
        System.out.println("CPU---Hp");
    }

    @Override
    public void Motherboard() {
        System.out.println("主板---Hp");
    }

    @Override
    public void Radiator() {
        System.out.println("散热器---Hp");
    }

    @Override
    public void RAM() {
        System.out.println("内存---Hp");
    }

    @Override
    public void GPU() {
        System.out.println("GPU---Hp");
    }

    @Override
    public void HDD() {
        System.out.println("HDD---Hp");
    }

    @Override
    public void PowerSupply() {
        System.out.println("电源---Hp");
    }

    @Override
    public void MachineBox() {
        System.out.println("机箱---Hp");
    }

    @Override
    public void Assemble() {
        System.out.println("组装完成---Hp");
    }
}


//联想
public class LenovoComputer implements Computer {


    @Override
    public void CPU() {
        System.out.println("CPU---Lenovo");
    }

    @Override
    public void Motherboard() {
        System.out.println("主板---Lenovo");
    }

    @Override
    public void Radiator() {
        System.out.println("散热器---Lenovo");
    }

    @Override
    public void RAM() {
        System.out.println("内存---Lenovo");
    }

    @Override
    public void GPU() {
        System.out.println("GPU---Lenovo");
    }

    @Override
    public void HDD() {
        System.out.println("HDD---Lenovo");
    }

    @Override
    public void PowerSupply() {
        System.out.println("电源---Lenovo");
    }

    @Override
    public void MachineBox() {
        System.out.println("机箱---Lenovo");
    }

    @Override
    public void Assemble() {
        System.out.println("组装完成---Lenovo");
    }
}


// 简单工厂
public class SimpleComputerFactory {

    public Computer createComputer(ComputerEnum computerEnum){
        return SelectComputer(computerEnum);
    }

    private Computer SelectComputer(ComputerEnum computerEnum) {
        switch (computerEnum) {
            case MSI:
                return new MsiComputer();
            case Hp:
                return new HpComputer();
            case Lenovo:
                return new LenovoComputer();
            default:
                return null;
        }
    }
}

从代码上可以看出 SimpleComputerFactory 其实是将Computer对象的创建过程抽出来,根据输入参数ComputerEnum枚举类型来判断返回什么品牌的电脑,先看调用及返回,再来聊聊这种方式的利弊

首先是调用方法

    @Test
    public void testSimpleFactory(){
        SimpleComputerFactory simpleComputerFactory=new SimpleComputerFactory();
        Computer msiComputer=simpleComputerFactory.createComputer(ComputerEnum.MSI);//想要生产一台MSI
        if (!StringUtils.isEmpty(msiComputer)){//使用简单工厂生产出来的对象可能为NULL,这里需要做一个判断
            //查看电脑配置
            msiComputer.CPU();
            msiComputer.Motherboard();
            msiComputer.Radiator();
            msiComputer.RAM();
            msiComputer.GPU();
            msiComputer.HDD();
            msiComputer.PowerSupply();
            msiComputer.MachineBox();
            msiComputer.Assemble();
        }
    }

这里调用简单工厂生产一台MSI,参数传入的是MSI枚举,如果想生产惠普则传入Hp枚举值,来看下输出结果:

CPU---MSI
主板---MSI
散热器---MSI
内存---MSI
GPU---MSI
HDD---MSI
电源---MSI
机箱---MSI
组装完成---MSI

没生产错,问题不大.只要没传错参数就能得到结果.就目前情况来看,还算满足,这个简单工厂可以生产三种品牌的电脑,惠普,联想,微星.但如果我想要一台戴尔呢?别无他法,只能改这个简单工厂,在switch条件中添加一种,并且去维护枚举中的值,添加一个戴尔.下次如果想添加新的品牌,也是这个流程,这就意味着,这个简单工厂可能会被频繁的改动.这便是简单工厂的弊端.

那么解决办法呢?那就来看看工厂模式中的工厂方法

解决方法很简单,让工厂专注干一件事,比如需要MSI电脑就创建MSI的工厂,需要Hp就创建Hp的工厂

详情先看下面的代码,这次将Computer对象换了一种方式约束,广义上的实现接口,指的是实现某一个超类型的某个方法,这个超类型,可以是类也可以是定义的接口.


//首先是定义电脑类型的超类
@Data
public abstract class ComputerModel {

    //CPU
    private String CPU;

    //主板
    private String Motherboard;

    //散热器
    private String Radiator;

    //内存条
    private String RAM;

    //显卡
    private String GPU;

    //硬盘
    private String HDD;

    //电源
    private String PowerSupply;

    //机箱
    private String MachineBox;

    //组装电脑
    public abstract void assemble();
}


//然后是MSI的电脑类型
public class MsiComputer extends ComputerModel {

    public MsiComputer(String CPU,String GPU){
        //打上MSI的标记
        setCPU(CPU);
        setMotherboard("Motherboard---MSI");
        setRadiator("Radiator---MSI");
        setRAM("RAM---MSI");
        setGPU(GPU);
        setHDD("HDD---MSI");
        setPowerSupply("PowerSupply---MSI");
        setMachineBox("MachineBox---MSI");
    }

    @Override
    public void assemble() {
        System.out.println("----来自MSI工厂流水线操作---");
        System.out.println(this.toString());
    }
}


//电脑工厂接口
public interface ComputerFactory {
     ComputerModel getComputer();
}


//MSI工厂
public class MsiComputerFactory implements ComputerFactory {
    @Override
    public ComputerModel getComputer() {
        return new MsiComputer("i7-9700K","RTX2070");
    }
}

这里声明了一个电脑类型,里面包括若干属性及一个方法,同时定义了MSI的工厂,先看调用方法逻辑

    @Test
    public void testFactory(){
        /*
        如果直接创建的话
        ComputerModel msiComputer=new MsiComputer();
        这就对MsiComputer本身产生了依赖,
        而使用工厂的话则不需要关注这些,将组装等操作从调用方法这解耦出去,依赖倒置
         */
        ComputerFactory msiComputerFactory=new MsiComputerFactory();//创建一个Msi工厂
        ComputerModel msiComputer=msiComputerFactory.getComputer();//工厂生产Msi电脑
        //如果需要惠普电脑或联想电脑,在这里引入惠普或联想工厂即可
        msiComputer.assemble();//组装并查看
    }


    @Test
    public void testMsi(){
        String CPU="i7-9700K";
        String GPU="RTX2070";
        ComputerModel msi=new MsiComputer(CPU,GPU);
        msi.assemble();
    }

这里的两个测试方法都可以创建出一个MsiComputer对象,但区别在哪呢?

答案是没有区别.

这两个方法创建的对象属性值都一样,但是使用工厂方法的话可以解耦,就如同我在代码注释中所写一样,使用工厂方法创建对象时对MsiComputer对象没有依赖,而第二个测试方法,里面不仅仅依赖了MsiComputer对象还对CPU,GPU对象有依赖,这就意味着一旦产品有变动,比如我想换RTX2080了,就需要修改这里的GPU对象.而工厂方法则不需要关注这些,这便是解耦,那依赖倒置怎么解释呢?从第二个测试方法来看,testMsi方法为调用客户端,它依赖了CPU,GPU,MsiComputer,如果创建更多则可能还会依赖HpComputer,LenovoComputer,而和testMsi同样能创建电脑对象的工厂方法呢?testFactory依赖了 ComputerModel,而ComputerModel 是其他具体电脑类型的超类,具体类型的电脑(MsiComputer,HpComputer,LenovoComputer)依赖ComouterModel,这样就依赖倒置了,有空我把图补上.总结起来,本来高层(testFactory方法)依赖所有低层对象(MsiComputer等),使用工厂方法后变成了高层依赖一个超类(ComputerModel),而其他低层也依赖这个超类,从依赖关系上来看,倒置了,这便是依赖倒置.

说回工厂方法,工厂方法可以解耦,依赖倒置,但是添加了其他类型的产品后就必须添加新的工厂.

聊完工厂方法,再说说抽象方法,和工厂方法对比,抽象工厂其实更加突出产品组合.工厂方法生产一件产品,而抽象工厂生产有关联的一族产品

老样子,先看一组代码,下面修改了电脑的构造方法,将CPU,GPU,主板参数传入.并且定义了电脑配件工厂,里面定义了CPU,GPU,主板的生产方法


//电脑工厂接口
public interface ComputerFactory {
     ComputerModel getComputer(ComputerPartsFactory computerPartsFactory);
}


//电脑组件工厂接口
public interface ComputerPartsFactory {

    String getCPU();

    String getGpu();

    String getMotherboard();

}


//MSI电脑工厂
public class MsiComputerFactory implements ComputerFactory {
    @Override
    public ComputerModel getComputer(ComputerPartsFactory computerPartsFactory) {
        String CPU=computerPartsFactory.getCPU();//从工厂拿到CPU
        String GPU=computerPartsFactory.getGpu();//从工厂拿到GPU
        String Motherboard=computerPartsFactory.getMotherboard();//从工厂拿到主板
        return new MsiComputer(CPU,GPU,Motherboard);
    }
}


//MSI电脑产品,这里修改了构造方法
public class MsiComputer extends ComputerModel {

    public MsiComputer(String CPU,String GPU,String Motherboard){
        setCPU(CPU);
        setGPU(GPU);
        setMotherboard(Motherboard);
    }

    @Override
    public void assemble() {
        System.out.println("----来自MSI工厂流水线操作---");
        System.out.println(this.toString());
    }
}


//游戏版电脑配件工厂
public class GameComputerPartsFactory implements ComputerPartsFactory {

    @Override
    public String getCPU() {
        return "i7 9700K---Game";
    }

    @Override
    public String getGpu() {
        return "RTX 2070---Game";
    }

    @Override
    public String getMotherboard() {
        return "Z390H---Game";
    }
}


//学习版电脑配件工厂
public class StudyComputerPartsFactory implements ComputerPartsFactory {

    @Override
    public String getCPU() {
        return "i5 7500---Study";
    }

    @Override
    public String getGpu() {
        return "GTX 1050---Study";
    }

    @Override
    public String getMotherboard() {
        return "B360---Study";
    }
}


再看调用方法

    @Test
    public void testAbstractFactory(){
        ComputerFactory msiComputerFactory=new MsiComputerFactory();//创建一个Msi工厂
        ComputerPartsFactory gamePartsFactory=new GameComputerPartsFactory();//创建一个游戏配件厂
        ComputerPartsFactory studyPartsFactory=new StudyComputerPartsFactory();//创建一个游戏配件厂
        ComputerModel msiComputerGame=msiComputerFactory.getComputer(gamePartsFactory);//工厂生产Msi电脑游戏版
        ComputerModel msiComputerStudy=msiComputerFactory.getComputer(studyPartsFactory);//工厂生产Msi电脑学习版
        System.out.println("-----------游戏版----------");
        msiComputerGame.assemble();//组装并查看
        System.out.println("-----------学习版----------");
        msiComputerStudy.assemble();
    }

运行结果如下:

-----------游戏版----------
----来自MSI工厂流水线操作---
ComputerModel(CPU=i7 9700K---Game, Motherboard=Z390H---Game, Radiator=null, RAM=null, GPU=RTX 2070---Game, HDD=null, PowerSupply=null, MachineBox=null)
-----------学习版----------
----来自MSI工厂流水线操作---
ComputerModel(CPU=i5 7500---Study, Motherboard=B360---Study, Radiator=null, RAM=null, GPU=GTX 1050---Study, HDD=null, PowerSupply=null, MachineBox=null)

从这里可以看出,抽象工厂将同一组(游戏版,学习版)的产品组合在一起,创建对象使用的是工厂方法.如果需要修改产品族类型,修改工厂的实现方法就行,不需要修改其他代码.但是抽象工厂也存在着问题,比如说,我觉得光只有CPU,GPU和主板三个组件不太够,现在希望把内存也维护进去,那么麻烦事就来了,在抽象工厂(ComputerPartsFactory)里添加一个方法,意味着所有实现类(GameComputerPartsFactory,StudyComputerPartsFactory等)全部需要修改,这对程序维护来说是非常麻烦的.


总结一下,简单工厂不属于工厂模式,他是一种编码习惯;工厂方法可以将客户端代码从需要实例化的具体类中解耦,可以依赖倒置;抽象工厂可以将一族相关产品集合起来,经常与工厂方法结合使用,能够有效解耦.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值