Java设计模式--工厂模式:对象创建的魔法工坊

Java工厂模式:对象创建的魔法工坊

一、引言:欢迎来到工厂模式的奇妙世界

嘿,各位 Java 开发者小伙伴们!今天咱一头扎进设计模式里超有意思的 “工厂模式”。先别被 “设计模式” 这高大上的词唬住,其实它就是前辈们总结出来,帮咱们写代码更顺、更易维护、更灵活的套路。就好比盖房子,要是没个好蓝图,东一榔头西一棒槌,那房子不得塌咯!设计模式就是咱代码世界里的 “超级蓝图”,工厂模式更是其中超实用的一个。

你想想,生活里工厂是干啥的?汽车工厂生产汽车,手机工厂造手机。工厂就像个神奇的 “魔法盒子”,你给它点原料、要求,它就能变出你想要的产品。Java 里的工厂模式差不多也是这道理,专门负责 “生产” 对象 。为啥要专门弄个 “工厂” 来创建对象呢?直接new一下不香吗?别急,听我慢慢道来。

要是你的代码简单得像儿歌,那直接new确实没啥问题。但一旦项目变得像《哈利・波特》系列小说那么庞大复杂,对象创建逻辑到处都是,那可就乱成一锅粥了。好比你走进一个杂乱无章的仓库,找个零件比登天还难。工厂模式就像给这个仓库来了个 “大整理”,把对象创建的活儿都集中到一个地方,就像把零件都分类摆好,要用的时候直接去对应的 “货架” 拿就行,是不是方便多啦?

而且工厂模式还能降低代码间的 “亲密过度” 问题,也就是耦合度。你和朋友太亲密,啥事都掺和,一旦闹掰,影响可大了。代码也一样,要是各个部分紧密相连,改一处,到处都得跟着 “折腾”。工厂模式把对象创建封装起来,其他代码只需要和工厂 “打交道”,不用关心对象具体怎么创建,这样代码间就保持了 “恰到好处” 的距离,维护起来轻松多了。

再举个例子,你开一家餐厅,菜单上有各种美食。要是没有工厂模式,每次有人点汉堡,厨房就得临时找食材、想做法,忙得晕头转向;有了工厂模式,就像有个专门的 “汉堡工厂”,只要把订单传给它,它就按标准化流程做出美味汉堡,效率大大提高,还保证品质稳定。在 Java 开发里,工厂模式能帮我们提高代码复用性、可维护性,让代码更健壮、更灵活。

接下来,咱就深入工厂模式的奇妙世界,看看简单工厂模式、工厂方法模式、抽象工厂模式这三兄弟到底有啥本事,能在 Java 开发里 “大显神通”!

二、工厂模式基础入门

2.1 什么是工厂模式

工厂模式,从名字就能看出它和现实中的工厂有点像。在现实里,工厂是生产产品的地方,像汽车工厂能生产各种各样的汽车 。在 Java 世界中,工厂模式就是把对象的创建和使用分开,就好比你去买车,不用关心汽车是怎么生产的,只要从汽车工厂提车就行,汽车工厂负责把汽车生产好,你只需要使用汽车这个产品。

用代码的方式来解释下。假设我们有一个Car接口,它代表汽车,然后有BenzCarAudiCar这两个类实现了Car接口,分别表示奔驰汽车和奥迪汽车。如果没有工厂模式,我们在使用汽车的时候,就需要在代码里自己new出具体的汽车对象,就像这样:

// 没有工厂模式时创建并使用汽车对象
Car benz = new BenzCar();
benz.run();

Car audi = new AudiCar();
audi.run();

但是,当我们使用工厂模式时,就会有一个CarFactory工厂类来帮我们创建汽车对象,代码就变成了这样:

// 使用工厂模式创建并使用汽车对象
CarFactory carFactory = new CarFactory();
Car benz = carFactory.createCar("benz");
benz.run();

Car audi = carFactory.createCar("audi");
audi.run();

在这个例子里,CarFactory就是工厂类,它把创建BenzCarAudiCar对象的逻辑封装起来了。我们使用汽车的代码就变得简单了,只需要告诉工厂类我们想要什么类型的汽车,工厂类就会帮我们创建好,这就是工厂模式的基本思想。

2.2 为什么要用工厂模式

你可能会想,不用工厂模式,直接new对象多简单呀,为啥还要用工厂模式这么复杂的东西呢?其实,在简单的项目里,直接new对象确实没问题,但当项目越来越大,代码越来越复杂,不用工厂模式就会出现很多问题。

代码重复问题

假如我们的项目里有很多地方都需要创建BenzCar对象,每次都要写new BenzCar(),如果创建BenzCar对象的逻辑变了,比如需要给BenzCar对象设置一些初始参数,那就得在所有创建BenzCar对象的地方都修改代码,这就很容易出错,而且维护起来特别麻烦。

// 多处创建BenzCar对象,代码重复
Car benz1 = new BenzCar();
// 其他代码
Car benz2 = new BenzCar();
// 更多代码
Car benz3 = new BenzCar();
修改困难

还是上面的例子,如果我们要把BenzCar的创建逻辑改成从配置文件读取参数来创建,那所有创建BenzCar对象的代码都得改,牵一发而动全身,说不定还会改出其他的 bug。而且,如果创建BenzCar对象的逻辑特别复杂,比如涉及到数据库查询、网络请求等,把这些复杂的逻辑分散在各个地方,代码就会变得一团糟,可读性和可维护性都很差。

工厂模式来救场

使用工厂模式就能很好地解决这些问题。把对象的创建逻辑都放在工厂类里,其他地方只需要调用工厂类的方法来获取对象,这样代码就变得简洁、易维护。如果创建对象的逻辑变了,只需要在工厂类里修改,其他使用对象的地方完全不需要改动,就像给汽车工厂升级了生产设备,生产流程变了,但我们从工厂提车的方式还是一样的 。同时,工厂模式也符合 “单一职责原则”,工厂类只负责创建对象,其他类只负责使用对象,每个类的职责明确,代码结构更加清晰。这样,我们的代码就像一个有序的工厂车间,每个环节都分工明确,运行起来更加高效、稳定。

三、简单工厂模式:工厂模式的初体验

3.1 模式定义与结构

简单工厂模式,虽说严格来讲它不算 23 种经典设计模式里的 “正式成员”,但绝对是工厂模式里最基础、最好上手的 “入门款”,就像游戏里的新手教程关卡 。它又被叫做静态工厂方法模式,为啥呢?因为它主要靠工厂类里的静态方法来创建对象。

在简单工厂模式里,主要有三个 “角色” :

  • 工厂类(Creator):这可是模式的 “大当家”,掌握着创建所有产品对象的核心逻辑。就像汽车工厂的总控制台,决定生产什么汽车。它提供一个静态方法,这个方法就像是工厂的 “接单窗口”,外界调用这个方法,告诉它想要什么产品,它就按照内部逻辑把产品生产出来并返回 。

  • 抽象产品类(Product):它是所有具体产品的 “老大哥”,定义了这些产品共有的行为和属性,规定了产品要实现哪些功能。比如汽车都得有跑的功能,这个功能就会在抽象产品类里定义。

  • 具体产品类(Concrete Product):它们是抽象产品类的 “小弟们”,一个个具体的实现类,实现了抽象产品类定义的抽象方法,拥有各自独特的属性和行为。就像不同品牌的汽车,虽然都能跑,但性能、外观各不相同。

下面用一张图来直观感受下它们之间的关系 :

在这里插入图片描述

从图中可以看出,客户端和工厂类打交道,告诉工厂类要什么产品,工厂类根据客户端的请求,创建出对应的具体产品类对象,然后返回给客户端使用,客户端完全不用关心具体产品是怎么创建出来的,只需要使用产品的功能就行。

3.2 代码示例

咱们以生产电子产品为例,来看看简单工厂模式的代码实现。假设我们要生产手机和电脑,首先定义一个电子产品的抽象接口ElectronicProduct

// 抽象产品类:电子产品
public interface ElectronicProduct {
    // 电子产品都有开机功能
    void powerOn();
}

接着,创建具体的产品类,Phone类和Computer类,它们都实现了ElectronicProduct接口 :

// 具体产品类:手机
public class Phone implements ElectronicProduct {
    @Override
    public void powerOn() {
        System.out.println("手机开机,进入主界面");
    }
}

// 具体产品类:电脑
public class Computer implements ElectronicProduct {
    @Override
    public void powerOn() {
        System.out.println("电脑开机,加载系统");
    }
}

然后,就是最重要的工厂类ElectronicProductFactory登场了,它负责创建手机和电脑对象 :

// 工厂类:电子产品工厂
public class ElectronicProductFactory {
    // 静态方法创建电子产品对象
    public static ElectronicProduct createProduct(String productType) {
        if ("phone".equalsIgnoreCase(productType)) {
            return new Phone();
        } else if ("computer".equalsIgnoreCase(productType)) {
            return new Computer();
        } else {
            throw new IllegalArgumentException("不支持的产品类型:" + productType);
        }
    }
}

最后,来看看客户端是怎么使用这个工厂模式的 :

// 客户端测试类
public class Client {
    public static void main(String[] args) {
        // 创建手机对象
        ElectronicProduct phone = ElectronicProductFactory.createProduct("phone");
        phone.powerOn();

        // 创建电脑对象
        ElectronicProduct computer = ElectronicProductFactory.createProduct("computer");
        computer.powerOn();
    }
}

运行Client类的main方法,输出结果如下:

手机开机,进入主界面
电脑开机,加载系统

通过上面的代码,我们可以看到,客户端只需要调用ElectronicProductFactorycreateProduct方法,传入想要的产品类型,就能轻松获得对应的电子产品对象,而不用关心对象创建的复杂过程 。

3.3 优缺点分析

简单工厂模式就像一把双刃剑,既有闪闪发光的优点,也存在一些小小的 “瑕疵”。

优点
  • 实现简单,使用方便:工厂类把对象创建逻辑封装得严严实实,客户端只需要简单调用工厂类的静态方法,传入参数,就能拿到想要的产品对象,就像在自动售货机买饮料,投币选商品就行,完全不用管饮料是怎么从仓库到售货机的。

  • 提高代码的可维护性:因为对象创建逻辑都集中在工厂类里,如果创建对象的逻辑需要修改,比如要给手机对象增加一些初始化设置,只需要在工厂类里改,其他使用手机对象的地方完全不用动,大大降低了维护成本,就好比汽车工厂改进生产工艺,不影响我们开车。

  • 一定程度的解耦:客户端和具体产品类之间的依赖关系被工厂类 “隔开” 了,客户端只依赖工厂类和抽象产品类,降低了耦合度,让代码结构更清晰,可扩展性更强。比如以后要新增一种电子产品平板,只需要在工厂类里添加创建平板的逻辑,客户端代码基本不用改。

缺点
  • 不符合开闭原则:开闭原则就是对扩展开放,对修改关闭。简单工厂模式里,一旦要增加新的产品类型,比如要生产电视,就必须修改工厂类的createProduct方法,添加创建电视对象的逻辑,这就违反了开闭原则。就像一个已经装修好的房子,每次想添加新家具都得拆墙打洞,太折腾了。

  • 工厂类职责过重:工厂类承担了所有产品的创建任务,随着产品类型越来越多,工厂类的createProduct方法里的if-else判断逻辑会变得超级复杂,代码越来越臃肿,可读性和可维护性都会变差,就像一个小工厂不断扩大业务,却不增加人手,老板一个人忙得焦头烂额。

3.4 适用场景

简单工厂模式虽然有缺点,但在一些特定场景下还是非常好用的。

  • 产品类型较少:当系统中需要创建的对象种类不多时,简单工厂模式的工厂类不会过于复杂,修改起来也相对容易。比如一个小型游戏中,只有几种简单的道具,用简单工厂模式来创建道具对象就很合适。

  • 创建逻辑简单:如果对象的创建逻辑比较简单,没有复杂的初始化、依赖注入等操作,简单工厂模式能很好地发挥它的优势,快速创建对象。

  • 不频繁变化:产品类型和创建逻辑相对稳定,不会频繁变动的情况下,使用简单工厂模式可以减少不必要的代码结构复杂度,提高开发效率。比如一个基础的数学运算库,只提供加、减、乘、除几种简单运算,用简单工厂模式创建运算对象就很合适,因为运算类型基本不会变。

四、工厂方法模式:更灵活的对象创建方式

4.1 模式定义与结构

简单工厂模式虽然好用,但在面对复杂多变的业务需求时,就有点 “力不从心” 了。这时候,工厂方法模式就闪亮登场啦,它可是简单工厂模式的 “进阶版”,能解决简单工厂模式不符合开闭原则的大问题 。

工厂方法模式定义:定义一个创建对象的接口,让子类决定实例化哪个具体类 。说通俗点,就是把简单工厂模式里工厂类创建对象的逻辑,分散到一个个具体的工厂子类中,每个子类负责创建一种特定的产品 。

在工厂方法模式里,有四个重要 “角色” :

  • 抽象工厂类(Creator):它就像一个 “抽象指挥官”,声明了一个工厂方法,这个方法是创建产品的 “总蓝图”,但具体怎么创建,它不管,交给子类去实现 。比如汽车生产总规划厂,只制定生产汽车的大致流程和标准,不负责具体生产。

  • 具体工厂类(Concrete Creator):是抽象工厂类的 “实干小弟”,实现了抽象工厂类声明的工厂方法,负责创建具体的产品对象 。像宝马汽车工厂,就按照总规划厂的标准,具体生产宝马汽车。

  • 抽象产品类(Product):是所有具体产品的 “共同模板”,定义了产品的通用行为和属性 。比如汽车都有行驶、刹车等功能,这些就会在抽象产品类里定义。

  • 具体产品类(Concrete Product):实现了抽象产品类的抽象方法,是一个个具体的产品 。比如宝马 X5、奥迪 A6 等具体型号的汽车。

用一张类图来看看它们之间的关系 :

在这里插入图片描述

从图中可以看出,客户端通过抽象工厂类来创建产品,具体创建哪个产品,由具体工厂类决定 。当需要增加新的产品时,只需要增加一个具体产品类和对应的具体工厂类,不需要修改抽象工厂类和其他具体工厂类的代码,完美符合开闭原则 。

4.2 代码示例

咱们接着用前面生产电子产品的例子,把简单工厂模式改造成工厂方法模式 。首先,抽象产品类ElectronicProduct和具体产品类PhoneComputer还是不变:

// 抽象产品类:电子产品
public interface ElectronicProduct {
    // 电子产品都有开机功能
    void powerOn();
}

// 具体产品类:手机
public class Phone implements ElectronicProduct {
    @Override
    public void powerOn() {
        System.out.println("手机开机,进入主界面");
    }
}

// 具体产品类:电脑
public class Computer implements ElectronicProduct {
    @Override
    public void powerOn() {
        System.out.println("电脑开机,加载系统");
    }
}

然后,定义抽象工厂类ElectronicProductFactory

// 抽象工厂类:电子产品工厂
public abstract class ElectronicProductFactory {
    // 抽象的工厂方法,用于创建电子产品
    public abstract ElectronicProduct createProduct();
}

接着,创建具体工厂类PhoneFactoryComputerFactory,它们分别负责创建手机和电脑:

// 具体工厂类:手机工厂
public class PhoneFactory extends ElectronicProductFactory {
    @Override
    public ElectronicProduct createProduct() {
        return new Phone();
    }
}

// 具体工厂类:电脑工厂
public class ComputerFactory extends ElectronicProductFactory {
    @Override
    public ElectronicProduct createProduct() {
        return new Computer();
    }
}

最后,看看客户端是怎么使用的 :

// 客户端测试类
public class Client {
    public static void main(String[] args) {
        // 使用手机工厂创建手机对象
        ElectronicProductFactory phoneFactory = new PhoneFactory();
        ElectronicProduct phone = phoneFactory.createProduct();
        phone.powerOn();

        // 使用电脑工厂创建电脑对象
        ElectronicProductFactory computerFactory = new ComputerFactory();
        ElectronicProduct computer = computerFactory.createProduct();
        computer.powerOn();
    }
}

运行Client类的main方法,输出结果和简单工厂模式一样:

手机开机,进入主界面
电脑开机,加载系统

对比简单工厂模式,工厂方法模式把创建对象的逻辑分散到了具体工厂类中。如果以后要增加新的电子产品,比如平板,只需要新增一个Tablet具体产品类和TabletFactory具体工厂类,客户端代码基本不用改,是不是方便多啦 。

4.3 优缺点分析

工厂方法模式作为简单工厂模式的 “升级版”,优点那是相当突出,但也有一些小缺点。

优点
  • 符合开闭原则:这可是工厂方法模式的 “高光时刻”。当需要增加新的产品时,只需要新增具体产品类和对应的具体工厂类,不用修改现有的代码 。就像一家餐厅要增加新菜品,不用把厨房布局、菜单排版都改一遍,直接加个新菜谱和对应的厨师就行,对原有的业务几乎没有影响。

  • 满足单一职责原则:每个具体工厂类只负责创建一种产品,职责明确 。不像简单工厂模式里,一个工厂类要管所有产品的创建,现在每个工厂各司其职,代码结构更清晰,维护起来也轻松。

  • 解耦效果更好:客户端和具体产品类的耦合度更低了,客户端只和抽象工厂类打交道,不关心具体产品是怎么创建的 。就像你网购商品,只需要在购物平台下单,不用关心商品是哪个仓库发货、怎么运输过来的,购物平台就相当于抽象工厂类,帮你把这些细节都屏蔽了。

缺点
  • 工厂类数量增多:每增加一种产品,就需要增加一个具体工厂类,导致工厂类的数量越来越多 。就像一个小镇,本来只有几家工厂,随着产业发展,工厂越来越多,管理起来就有点麻烦了。在代码里,过多的工厂类也会增加代码的管理难度和维护成本。

  • 增加代码复杂度:相比简单工厂模式,工厂方法模式引入了抽象工厂类,代码结构更复杂 。对于简单的业务场景,可能有点 “杀鸡用牛刀” 的感觉,反而增加了不必要的开发成本。

4.4 适用场景

工厂方法模式适用于以下场景:

  • 产品种类较多且变化频繁:当系统中产品类型不断增加和变化时,工厂方法模式的开闭原则优势就能充分发挥 。比如电商系统,商品种类繁多,而且不断有新商品上架,使用工厂方法模式,每个商品类型对应一个工厂类,新增商品时很方便扩展。

  • 创建对象逻辑复杂:如果创建对象的过程涉及复杂的初始化、资源获取等操作,把这些逻辑封装在具体工厂类中,可以使代码更清晰,便于维护 。比如创建数据库连接对象,需要配置各种参数、加载驱动等,用工厂方法模式把这些复杂逻辑放在工厂类里,其他代码调用起来就简单多了。

  • 需要灵活扩展和维护:工厂方法模式的设计结构使得代码具有良好的扩展性和维护性,适合大型项目的开发 。在大型项目中,需求经常变动,使用工厂方法模式可以降低代码修改的风险,提高开发效率。

五、抽象工厂模式:创建产品族的神器

5.1 模式定义与结构

抽象工厂模式,堪称工厂模式家族里的 “终极形态”,它就像一个超级工厂的大总管,能创建一系列相关或相互依赖的对象,还不用指定具体类 。啥意思呢?打个比方,你开一家家居店,店里有现代风格和古典风格的家具系列。每个系列里都有沙发、茶几、电视柜这些家具。抽象工厂模式就能帮你轻松管理这些不同风格的家具系列的创建,你不用关心每个家具具体是怎么生产出来的,只要告诉工厂你要哪种风格的家具系列就行 。

在抽象工厂模式里,有四个关键 “角色” :

  • 抽象工厂(Abstract Factory):这是整个模式的 “大脑”,它声明了创建一系列产品的抽象方法,每个方法对应一种产品 。就像家居店的总设计师,规划好了生产各种家具的蓝图,但不负责具体制作。

  • 具体工厂(Concrete Factory):是抽象工厂的 “执行者”,实现了抽象工厂声明的抽象方法,负责创建具体的产品对象 。比如现代风格家具工厂,按照总设计师的蓝图,具体生产出现代风格的沙发、茶几等家具。

  • 抽象产品(Abstract Product):为每种产品定义了通用的接口,规定了产品应该具备的功能 。比如家具都应该有摆放、使用的功能,这些功能就会在抽象产品接口里定义。

  • 具体产品(Concrete Product):实现了抽象产品接口,是一个个具体的产品 。像现代风格的沙发、古典风格的茶几,它们都实现了家具的通用接口,又有各自独特的风格和特点。

用一张复杂点的类图来展示它们之间的关系,能看得更清楚 :

在这里插入图片描述

从图中可以看到,客户端通过抽象工厂来创建产品,具体创建哪些产品,由具体工厂决定。不同的具体工厂可以创建出不同风格或类型的产品族,这些产品族中的产品相互关联、相互配合 。比如现代风格家具工厂创建的沙发、茶几、电视柜,它们在风格、材质等方面相互协调,形成一个完整的现代风格家具产品族 。

5.2 代码示例

咱们以生产电脑硬件为例,来看看抽象工厂模式的代码实现。假设我们要生产不同品牌的电脑硬件,每个品牌都有自己的 CPU、内存和硬盘。

首先,定义抽象产品接口CPUMemoryHardDisk

// 抽象产品:CPU
public interface CPU {
    void run();
}

// 抽象产品:内存
public interface Memory {
    void storage();
}

// 抽象产品:硬盘
public interface HardDisk {
    void save();
}

接着,创建具体产品类,比如IntelCPUSamsungMemoryWesternDigitalHardDisk等,它们分别实现了对应的抽象产品接口 :

// 具体产品:Intel CPU
public class IntelCPU implements CPU {
    @Override
    public void run() {
        System.out.println("Intel CPU 高速运行");
    }
}

// 具体产品:三星内存
public class SamsungMemory implements Memory {
    @Override
    public void storage() {
        System.out.println("三星内存 高效存储");
    }
}

// 具体产品:西部数据硬盘
public class WesternDigitalHardDisk implements HardDisk {
    @Override
    public void save() {
        System.out.println("西部数据硬盘 稳定保存数据");
    }
}

然后,定义抽象工厂接口ComputerHardwareFactory

// 抽象工厂:电脑硬件工厂
public interface ComputerHardwareFactory {
    CPU createCPU();
    Memory createMemory();
    HardDisk createHardDisk();
}

再创建具体工厂类,比如IntelFactory,它实现了ComputerHardwareFactory接口,负责创建 Intel 品牌的电脑硬件 :

// 具体工厂:Intel工厂
public class IntelFactory implements ComputerHardwareFactory {
    @Override
    public CPU createCPU() {
        return new IntelCPU();
    }

    @Override
    public Memory createMemory() {
        return new SamsungMemory();
    }

    @Override
    public HardDisk createHardDisk() {
        return new WesternDigitalHardDisk();
    }
}

最后,看看客户端是怎么使用的 :

// 客户端测试类
public class Client {
    public static void main(String[] args) {
        // 使用Intel工厂创建电脑硬件
        ComputerHardwareFactory intelFactory = new IntelFactory();
        CPU cpu = intelFactory.createCPU();
        Memory memory = intelFactory.createMemory();
        HardDisk hardDisk = intelFactory.createHardDisk();

        cpu.run();
        memory.storage();
        hardDisk.save();
    }
}

运行Client类的main方法,输出结果如下:

Intel CPU 高速运行
三星内存 高效存储
西部数据硬盘 稳定保存数据

通过上面的代码,我们可以看到,客户端只需要通过抽象工厂接口来创建产品,不需要关心具体产品的创建细节,而且可以轻松切换不同品牌的硬件工厂,比如以后要使用 AMD 品牌的硬件,只需要创建一个AMDFactory具体工厂类就行,客户端代码基本不用改 。

5.3 优缺点分析

抽象工厂模式作为工厂模式的 “高阶版本”,优点很突出,但也有一些不可避免的缺点。

优点
  • 解耦客户端与具体实现:客户端只和抽象工厂、抽象产品打交道,和具体产品类的实现细节完全隔离,降低了耦合度 。就像你去买家具,只需要告诉店员你要现代风格还是古典风格的家具,不用关心家具是哪个厂家生产的、用什么材料做的,店员(抽象工厂)会帮你搞定一切。

  • 便于切换产品系列:如果要切换产品系列,比如从生产现代风格家具切换到生产古典风格家具,只需要更换具体工厂类就行,客户端代码几乎不用修改 。这就好比你原来卖苹果手机,现在想卖华为手机,只需要换个进货渠道(具体工厂),店铺的销售流程(客户端代码)基本不变。

  • 保证产品一致性:抽象工厂模式能确保创建出来的一系列产品属于同一个产品族,它们之间的兼容性和一致性有保障 。比如一套现代风格的家具,沙发、茶几、电视柜的风格肯定是统一的,不会出现风格混乱的情况。

缺点
  • 扩展困难:如果要增加新的产品类型,比如在电脑硬件里增加显卡,那就需要修改抽象工厂接口,并且所有的具体工厂类都要实现新增的创建显卡的方法 。这就像一个已经建好的工厂生产线,要增加新的产品,就得把整个生产线都改造一遍,成本很高。

  • 增加系统复杂度:抽象工厂模式涉及多个抽象层和具体类,类的数量增多,代码结构变得复杂 。对于简单的业务场景,使用抽象工厂模式可能会让代码变得过于复杂,增加开发和维护的难度。

5.4 适用场景

抽象工厂模式适用于以下场景:

  • 系统需要创建多个相关产品对象,形成产品族:当系统中存在多个相互关联的产品对象,并且需要将它们组合成一个产品族时,抽象工厂模式能很好地发挥作用 。比如游戏开发中,不同场景下的游戏元素,像森林场景里的树木、怪物、道具等,它们相互关联,形成一个森林场景的产品族,就可以用抽象工厂模式来创建。

  • 不依赖产品创建和表示细节:如果客户端只关心产品的功能,不关心产品是怎么创建和具体表示的,抽象工厂模式可以将这些细节封装起来 。比如开发一个跨平台的应用,不同平台的界面组件(按钮、文本框等)创建方式不同,但客户端只需要使用这些组件的功能,不需要知道它们在不同平台下的创建细节,这时就可以用抽象工厂模式。

六、三种工厂模式的对比与选择

6.1 对比分析

简单工厂模式、工厂方法模式和抽象工厂模式这三兄弟,虽然都属于工厂模式家族,但各自有着独特的 “性格” 和 “本领” 。下面我们从定义、结构、优缺点、适用场景等方面来全面对比一下它们 。

对比维度简单工厂模式工厂方法模式抽象工厂模式
定义定义一个工厂类,根据传入的参数创建不同类型的产品对象,它不是正式的设计模式,更像一种简单的创建对象方法定义一个创建对象的接口,让子类决定实例化哪个具体类,将对象创建延迟到子类中提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类,用于创建产品族
结构包含工厂类、抽象产品类、具体产品类三个角色,工厂类负责创建具体产品对象,通过一个静态方法根据参数决定创建哪个具体产品有抽象工厂类、具体工厂类、抽象产品类、具体产品类四个角色。抽象工厂类声明创建产品的抽象方法,具体工厂类实现抽象方法创建具体产品同样有抽象工厂、具体工厂、抽象产品、具体产品四个角色。抽象工厂声明创建一系列产品的抽象方法,具体工厂实现这些方法创建相关产品族
优点实现简单,使用方便;将对象创建逻辑封装,提高代码可维护性;一定程度解耦客户端和具体产品类符合开闭原则,便于扩展新产品;满足单一职责原则,每个具体工厂类职责明确;解耦效果更好,客户端只和抽象工厂类交互解耦客户端与具体实现,客户端不依赖具体产品类;便于切换产品系列,更换具体工厂即可;保证产品一致性,创建的产品属于同一产品族
缺点不符合开闭原则,增加新产品需修改工厂类代码;工厂类职责过重,随着产品增多,代码臃肿复杂工厂类数量增多,每增加一种产品就需新增一个具体工厂类;增加代码复杂度,相比简单工厂模式结构更复杂扩展困难,增加新的产品类型需修改抽象工厂接口及所有具体工厂类;系统复杂度增加,涉及多个抽象层和具体类
适用场景产品类型较少,创建逻辑简单,且产品类型和创建逻辑相对稳定,不频繁变化的场景产品种类较多且变化频繁,创建对象逻辑复杂,需要灵活扩展和维护的场景系统需要创建多个相关产品对象形成产品族,且不依赖产品创建和表示细节的场景

6.2 如何选择合适的工厂模式

面对这三种工厂模式,在实际项目中该怎么选择呢?其实很简单,就像选工具一样,要根据具体的 “活儿” 来挑 。

如果你的项目规模小,产品类型屈指可数,而且创建逻辑简单得就像 1+1,未来也没啥大变动的可能,那简单工厂模式绝对是你的 “贴心小棉袄”。它实现简单,能把对象创建逻辑封装起来,让你的代码看着整洁,用着顺手 。就好比你开个小便利店,商品种类少,进货渠道也简单,用简单工厂模式来管理商品进货就很合适。

要是项目规模逐渐壮大,产品类型像雨后春笋一样不断冒出来,创建逻辑也变得复杂多样,这时候工厂方法模式就该闪亮登场啦 。它符合开闭原则,新增产品时只需要增加具体产品类和对应的具体工厂类,不用去动已有的代码,就像给一个不断扩建的大楼增加房间,不影响其他房间的使用 。比如电商平台,商品种类繁多且经常更新,使用工厂方法模式就能轻松应对。

而当你的系统需要创建一系列相互关联的产品对象,形成一个完整的产品族,而且要保证这些产品之间的兼容性和一致性,抽象工厂模式就成了你的 “最佳拍档” 。它能把创建相关产品的逻辑封装起来,让你轻松切换不同的产品系列 。比如游戏开发中,不同主题的游戏元素(角色、道具、场景等)就可以用抽象工厂模式来创建,保证每个主题下的元素风格统一、相互协调。

总之,在选择工厂模式时,要综合考虑项目的规模、产品类型的数量和变化频率、创建对象的逻辑复杂度以及产品之间的关系等因素,选择最适合的工厂模式,让你的代码更加健壮、灵活、易维护 。

七、工厂模式在 Java 框架中的应用

7.1 Spring 框架中的工厂模式

在 Spring 框架这个庞大而神奇的 “代码帝国” 里,工厂模式可是扮演着举足轻重的角色,就像皇宫里的大管家,掌管着各种 Bean 的创建和管理 。其中,BeanFactoryApplicationContext就是 Spring 中运用工厂模式的典型代表 。

BeanFactory是 Spring 框架中最基础的 IoC(控制反转)容器接口,它就像一个超级工厂的 “原型”,定义了创建、获取和管理 Bean 的基本方法 。所有的 Spring 容器都必须实现这个接口,它为 Spring 框架提供了最核心的 Bean 管理功能 。比如说,当我们在 Spring 项目里定义了一个UserService类,并把它配置成一个 Bean,BeanFactory就负责把这个UserService类实例化,变成一个可以使用的对象 。它创建 Bean 的过程就像是按照图纸(Bean 定义)来生产产品(Bean 实例),保证每个 Bean 都能被正确创建并提供给需要的地方使用 。

下面是一个简单的示例,展示如何使用BeanFactory来创建和获取 Bean :

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class BeanFactoryDemo {
    public static void main(String[] args) {
        // 加载Spring配置文件
        Resource resource = new ClassPathResource("applicationContext.xml");
        // 创建BeanFactory
        BeanFactory beanFactory = new XmlBeanFactory(resource);

        // 从BeanFactory中获取Bean
        UserService userService = (UserService) beanFactory.getBean("userService");
        userService.sayHello();
    }
}

在这个例子里,XmlBeanFactoryBeanFactory的一个具体实现类,它通过读取applicationContext.xml配置文件来创建 BeanFactory 。然后,我们从beanFactory中获取userService这个 Bean,并调用它的sayHello方法 。这里的beanFactory就像是一个工厂的 “提货窗口”,我们只需要告诉它我们想要的 Bean 的名字,它就会把对应的 Bean 实例交给我们 。

ApplicationContext则是BeanFactory的 “豪华升级版”,它继承了BeanFactory接口,并在此基础上提供了更多强大的功能,比如国际化支持、事件发布、自动扫描组件等 。可以说,ApplicationContext是 Spring 框架中更高级、更常用的容器,它就像一个功能齐全的现代化工厂,不仅能生产产品,还能提供各种增值服务 。在实际的 Spring 项目中,我们更多地使用ApplicationContext来创建和管理 Bean 。

ClassPathXmlApplicationContext为例,它是ApplicationContext的一个实现类,用于从类路径下加载 Spring 配置文件 。下面是使用ClassPathXmlApplicationContext的示例代码 :

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextDemo {
    public static void main(String[] args) {
        // 创建ApplicationContext,加载Spring配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 从ApplicationContext中获取Bean
        UserService userService = applicationContext.getBean(UserService.class);
        userService.sayHello();
    }
}

在这个示例中,ClassPathXmlApplicationContext读取applicationContext.xml配置文件,创建了ApplicationContext 。通过applicationContext获取UserService Bean 时,我们甚至可以直接使用类型安全的方式获取,比BeanFactory更加便捷 。ApplicationContext在创建 Bean 时,还会自动完成 Bean 的初始化、依赖注入等操作,就像工厂在生产产品时,会自动为产品安装好各种零部件,让产品可以直接使用 。而且,ApplicationContext支持更多的配置方式,比如注解配置、Java 配置等,大大提高了开发的灵活性和效率 。

7.2 其他框架中的应用案例

除了 Spring 框架,工厂模式在其他 Java 框架中也有着广泛的应用 。比如 MyBatis,它是一个优秀的持久层框架,在创建SqlSession对象时就用到了工厂模式 。SqlSessionFactory就是 MyBatis 中的工厂类,负责创建SqlSession对象 。SqlSession是 MyBatis 中与数据库交互的核心接口,通过它我们可以执行 SQL 语句、提交事务等操作 。

SqlSessionFactory创建SqlSession的过程大致如下:首先,SqlSessionFactory通过读取 MyBatis 的配置文件,获取数据库连接信息、Mapper 映射关系等 。然后,它根据这些信息创建SqlSession对象 。在这个过程中,SqlSessionFactory就像一个专业的数据库连接工厂,把复杂的数据库连接创建和配置工作封装起来,我们只需要从它那里获取SqlSession对象,就可以轻松地与数据库进行交互了 。

下面是一个简单的 MyBatis 代码示例,展示SqlSessionFactory的使用 :

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisDemo {
    public static void main(String[] args) throws IOException {
        // 读取MyBatis配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 使用SqlSessionFactory创建SqlSession
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            // 执行SQL操作,这里省略具体的操作代码
            System.out.println("成功创建SqlSession,可进行数据库操作");
        }
    }
}

在这个例子中,SqlSessionFactoryBuilder负责构建SqlSessionFactory,它读取mybatis-config.xml配置文件来获取必要的信息 。然后,sqlSessionFactory通过openSession方法创建SqlSession 。整个过程清晰地展示了工厂模式在 MyBatis 中的应用,通过SqlSessionFactory这个工厂类,我们可以方便地创建和管理SqlSession,与数据库进行高效的交互 。

八、实战演练:在项目中运用工厂模式

8.1 案例背景

假设我们正在开发一个超火爆的在线教育平台,这个平台堪称知识的 “超级大超市”,里面有各种各样的课程,从编程语言 Python、Java,到艺术领域的绘画、音乐,再到职业技能的项目管理、市场营销,可谓应有尽有 。就像超市里有不同种类的商品一样,我们的在线教育平台也需要创建不同类型的课程对象,每个课程对象都有自己独特的属性和功能 。比如 Python 课程,它可能包含课程视频、练习题、在线答疑等功能;绘画课程可能有绘画教程视频、作品展示区等 。为了让平台的代码结构清晰、易于维护和扩展,我们决定引入工厂模式来创建这些课程对象 。

8.2 需求分析

在这个在线教育平台中,课程对象的创建可是个关键任务 。我们来仔细分析一下需求 。

首先,确定产品类型。我们有多种类型的课程,比如编程类课程(Python、Java 等)、艺术素养类课程(绘画、音乐等)、职业技能类课程(项目管理、市场营销等) 。每种类型的课程都有一些共同的属性和行为,比如都有课程名称、课程介绍、授课教师等属性,都有开始学习、暂停学习等行为 。所以,我们可以定义一个抽象的Course类,把这些共同的属性和行为放在里面 。然后,让具体的课程类,如PythonCoursePaintingCourseProjectManagementCourse等,继承这个抽象类,并实现自己独特的功能 。

接着,分析创建逻辑。创建课程对象时,需要根据不同的课程类型,加载不同的资源 。比如创建 Python 课程对象,需要加载 Python 课程的教学视频、代码示例、练习题等资源;创建绘画课程对象,需要加载绘画教程视频、优秀绘画作品展示等资源 。而且,创建课程对象可能还需要进行一些初始化操作,比如设置课程的价格、开课时间等 。

最后,考虑与其他模块的关系。课程对象创建好后,要和平台的其他模块进行交互 。比如,课程对象要和用户模块交互,记录用户的学习进度、学习成绩等信息;要和支付模块交互,处理课程购买的支付流程 。所以,在设计课程对象创建逻辑时,也要考虑到这些交互需求,确保课程对象能够顺利地与其他模块协同工作 。

8.3 设计与实现

经过前面的分析,我们决定采用工厂方法模式来实现课程对象的创建 。因为我们的课程类型较多,而且未来可能还会不断增加新的课程类型,工厂方法模式的开闭原则能很好地满足这个需求 。

先来看类图设计 :

在这里插入图片描述

从类图中可以看到,我们有一个抽象的Course类,它是所有课程的抽象基类,定义了课程的通用属性和方法 。然后有具体的课程类PythonCoursePaintingCourse等,它们继承自Course类,并实现了各自独特的功能 。接着,有一个抽象的CourseFactory类,它声明了一个抽象的createCourse方法,用于创建课程对象 。最后,有具体的工厂类PythonCourseFactoryPaintingCourseFactory等,它们继承自CourseFactory类,并实现了createCourse方法,负责创建具体的课程对象 。

下面是详细的 Java 代码实现 :

首先是抽象课程类Course

// 抽象课程类
public abstract class Course {
    protected String courseName;
    protected String courseIntroduction;
    protected String teacher;

    public Course(String courseName, String courseIntroduction, String teacher) {
        this.courseName = courseName;
        this.courseIntroduction = courseIntroduction;
        this.teacher = teacher;
    }

    // 开始学习方法
    public abstract void startLearning();

    // 暂停学习方法
    public abstract void pauseLearning();
}

然后是具体的课程类,以PythonCourse为例 :

// Python课程类
public class PythonCourse extends Course {
    public PythonCourse() {
        super("Python从入门到精通", "本课程全面讲解Python编程,从基础语法到高级应用", "张老师");
    }

    @Override
    public void startLearning() {
        System.out.println("开始学习Python课程,加载Python教学视频、代码示例...");
    }

    @Override
    public void pauseLearning() {
        System.out.println("暂停Python课程学习,记录学习进度...");
    }
}

接着是抽象工厂类CourseFactory

// 抽象课程工厂类
public abstract class CourseFactory {
    // 抽象的创建课程方法
    public abstract Course createCourse();
}

最后是具体的工厂类,以PythonCourseFactory为例 :

// Python课程工厂类
public class PythonCourseFactory extends CourseFactory {
    @Override
    public Course createCourse() {
        return new PythonCourse();
    }
}

客户端调用代码如下 :

// 客户端测试类
public class Client {
    public static void main(String[] args) {
        // 使用Python课程工厂创建Python课程对象
        CourseFactory pythonCourseFactory = new PythonCourseFactory();
        Course pythonCourse = pythonCourseFactory.createCourse();

        // 调用Python课程的方法
        pythonCourse.startLearning();
        pythonCourse.pauseLearning();
    }
}

运行Client类的main方法,输出结果如下 :

开始学习Python课程,加载Python教学视频、代码示例...
暂停Python课程学习,记录学习进度...

通过上面的代码,我们成功地使用工厂方法模式创建了课程对象 。当需要增加新的课程类型时,只需要新增一个具体的课程类和对应的具体工厂类,客户端代码基本不用修改,大大提高了代码的可维护性和扩展性 。

8.4 总结与反思

在这个在线教育平台项目中运用工厂模式,我们收获满满 。首先,代码的结构变得更加清晰,课程对象的创建逻辑被封装在工厂类中,其他模块只需要和工厂类交互,不用关心课程对象具体是怎么创建的,这降低了模块之间的耦合度 。其次,工厂方法模式的开闭原则让我们在增加新的课程类型时非常方便,只需要添加新的具体课程类和具体工厂类,符合项目不断发展和扩展的需求 。

不过,在项目实施过程中,我们也遇到了一些问题 。比如,当课程类型越来越多时,具体工厂类的数量也会随之增加,这在一定程度上增加了代码的管理难度 。为了解决这个问题,我们可以考虑使用反射机制来简化工厂类的实现 。通过反射,我们可以根据课程类型的名称动态地创建课程对象,这样就不需要为每个课程类型都创建一个具体的工厂类了 。另外,在创建课程对象时,资源的加载和初始化操作可能会比较耗时,我们可以考虑使用缓存机制,将已经创建好的课程对象缓存起来,下次需要时直接从缓存中获取,提高系统的性能 。

希望通过这个实战案例,能让大家对工厂模式在实际项目中的应用有更深入的理解和认识,在今后的开发中能够灵活运用工厂模式,打造出更加健壮、灵活、易维护的软件系统 。

九、总结与展望

9.1 回顾工厂模式要点

在 Java 开发的奇妙世界里,我们一起深入探索了工厂模式这个强大的工具 。从简单工厂模式、工厂方法模式到抽象工厂模式,它们各有千秋,为我们的代码编写带来了极大的便利 。

简单工厂模式就像一个初出茅庐的小工匠,虽然不是 23 种经典设计模式的正式成员,但却是我们踏入工厂模式大门的好帮手 。它通过一个工厂类,根据传入的参数创建不同类型的产品对象,把对象创建逻辑封装起来,让我们的代码在简单场景下实现起来轻松又便捷 。就像一个小便利店的老板,自己就能决定进什么货(创建什么产品),虽然商品种类不多,但管理起来很简单 。但它也有自己的小缺点,当产品种类增多时,工厂类就会变得臃肿,而且不符合开闭原则,每增加一个产品类型都得去修改工厂类的代码,就像小便利店要不断扩充商品种类,老板一个人就忙不过来了,还得经常调整进货策略 。

工厂方法模式则像是一群分工明确的工匠团队 。它定义了一个创建对象的接口,把创建对象的任务交给子类去完成,每个具体产品都有自己对应的工厂类 。这就好比一个稍微大一点的超市,不同种类的商品有不同的进货渠道(具体工厂类),新增商品种类时,只需要增加对应的进货渠道(具体工厂类)就行,不用去动其他商品的进货流程(已有代码),完全符合开闭原则 。而且每个具体工厂类只负责创建一种产品,职责明确,代码的可维护性和扩展性都大大提高 。不过,随着产品种类的不断增加,具体工厂类的数量也会跟着增多,这就需要我们好好管理这些 “小工匠”,不然代码就会变得有点复杂 。

抽象工厂模式堪称是一个超级工厂的大老板 。它能创建一系列相关或相互依赖的对象,形成产品族 。就像一个大型家居商场,里面有不同风格的家具系列,每个系列都包含沙发、茶几、电视柜等多种家具 。抽象工厂模式就负责创建这些不同风格的家具系列,保证每个系列里的家具相互协调、风格统一 。它把客户端和具体实现隔离开来,让我们可以轻松切换不同的产品系列,就像商场可以根据市场需求,随时更换不同风格的家具展示区 。但是,当需要增加新的产品类型时,比如要在家具系列里增加灯具,就需要修改抽象工厂接口以及所有的具体工厂类,这可是个大工程,就像商场要重新规划整个布局来增加新的商品种类 。

在实际的 Java 框架中,工厂模式也有着广泛的应用 。Spring 框架里的BeanFactoryApplicationContext就是运用工厂模式的典型代表,它们负责创建和管理各种 Bean,就像一个大型工厂的生产调度中心,让 Spring 项目的对象管理变得有条不紊 。MyBatis 框架在创建SqlSession对象时也用到了工厂模式,SqlSessionFactory就像一个专业的数据库连接工厂,帮我们把复杂的数据库连接创建和配置工作都搞定,我们只需要使用SqlSession对象就能和数据库愉快地交互了 。

9.2 未来学习方向

工厂模式只是设计模式这个庞大知识体系中的冰山一角 。设计模式是前辈们智慧的结晶,它们为我们解决各种复杂的软件设计问题提供了行之有效的方案 。希望大家在掌握了工厂模式之后,继续深入学习其他设计模式,比如单例模式、观察者模式、策略模式等等 。每个设计模式都有它独特的魅力和适用场景,就像一个装满了各种神奇工具的百宝箱,在不同的开发场景下,我们可以选择合适的设计模式来优化我们的代码,让代码更加健壮、灵活、易维护 。

同时,随着技术的不断发展,新的编程语言、框架和开发理念层出不穷 。我们也要时刻关注工厂模式在这些新技术、新框架中的应用和发展 。比如在新兴的微服务架构中,工厂模式如何更好地服务于微服务之间的对象创建和管理;在大数据处理框架中,工厂模式又能发挥怎样的作用 。不断学习和探索,才能让我们跟上技术发展的步伐,在 Java 开发的道路上越走越远 。相信通过不断地学习和实践,大家都能成为 Java 开发领域的高手,用设计模式这把利刃,攻克一个又一个的技术难题,创造出更加优秀的软件作品 !

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值