设计模式 创建者模式之 工厂模式

八股文(概念)

工厂模式通过提供一个接口或者抽象类来定义创建对象的方法,然后由具体的工厂类来实现这个接口,从而使得对象的创建过程与客户端代码解耦。工厂模式的主要目的是封装对象的创建过程,使得客户端代码不直接依赖于具体的类实现。这有助于提高系统的灵活性,因为客户端可以通过工厂类来获取所需的对象,而不需要知道对象的具体实现细节。

就好比我们去瑞幸买咖啡 我们(用户)去咖啡店(工厂)我们不用去考虑制作过程 只需要告诉咖啡小哥 什么品种 打包方式 即可 

在工厂模式中 有多种角色 由于 工厂模式也有适合于不同业务场景的实现方式所以一一介绍

先制定一个简单的需求 方便理解:根据用户的选择 提供不同的游戏(这个好像不太合理 但没办法 我的代码就是游戏 因为太贪玩 哈哈哈)

 简单工厂实现(静态工厂放一起说吧 就是加个关键字懂的都懂)

简单工厂模式主要有三个角色:

产品(Product):

  • 产品是工厂模式中创建的对象,通常是一个接口或抽象类,定义了具体产品的通用行为。

具体产品(Concrete Product):

  • 具体产品是产品接口或抽象类的具体实现。简单工厂通过实例化具体产品来创建对象。

工厂(Factory):

  • 工厂是负责创建产品对象的类。在简单工厂中,这个工厂通常包含一个或多个方法(静态工厂就是静态方法 这样调用时就不用new factory 对象),根据不同的条件创建并返回具体产品的实例。
上代码

首先 先定义一个游戏抽象类作为产品(因为都有打开和关闭的特性 这里就用了抽象类 没有用到接口 但其实接口 jdk1.8之后也可以定义 default方法 {md 我果然犟嘴自己反驳自己})

 * @CreateTime: 2024-01-23  10:34
 * @Description: 抽象类 用来定义游戏的接口 抽取共同的特点
 * @Version: 1.0
 */

public abstract class Game {
    abstract void play();

    public void start() {
        System.out.println("开始游戏");
    }

    public void end() {
        System.out.println("结束游戏");
    }

}

在定义 魔兽世界和穿越火线继承game类并作为具体产品

public class WoWGame extends Game{
    @Override
    void play() {
        System.out.println("魔兽世界启动");
    }
}

public class CFGame extends Game{
    @Override
    void play() {
        System.out.println("来吧,辽一运输船中门对狙");
    }
}

定义游戏工厂类作为工厂 这里是静态工厂 即为方法用static修饰 不用static即为普通的简单工厂

public class GameSimpleFactory {
    public static Game createGame(String gameName) {
        switch (gameName) {
            case "wow":
                return new WoWGame();
            case "cf":
                return new CFGame();
            default:
                throw new RuntimeException("没有该游戏");
        }
    }
}

最后定义一个类 main方法进行调用 模拟实际开发场景

public class Client {
    public static void main(String[] args) {
        Game game = GameSimpleFactory.createGame("cf");
        game.start();
        game.play();
        game.end();
     //开始游戏
     //来吧,辽一运输船中门对狙
     //结束游戏

    }
}

这样就完成了一个简单的静态工厂模式 在client来模拟我们的操作 即根据合适场景创建适合的对象并操作创建出来的对象 

上述代码已经实现了 需求 但是 也有显而易见的弊端:不能够扩展,如果需要扩展新的产品,需要修改工厂类 违背了开闭原则!!

但他的优点也是显而易见的:将创建和使用解耦,使得系统更灵活 如果有新的产品直接添加到工厂即可并且实现起来很简单,在实际开发中也更习惯使用这种 

针对于上述问题 (违背开闭原则)就引出了第二种解决方案

工厂方法参上

工厂方法有四个角色:

产品(Product):

  • 产品是工厂方法模式中创建的对象,通常是一个接口或抽象类,定义了具体产品的通用行为。

具体产品(Concrete Product):

  • 具体产品是产品接口或抽象类的具体实现。每个具体产品都有对应的工厂类。

工厂接口(Factory Interface):

  • 工厂接口定义了创建产品的方法,这个接口可以是一个抽象类或接口。每个具体工厂都要实现这个接口。

具体工厂(Concrete Factory):

  • 具体工厂是实现工厂接口的类,负责实际创建具体产品的对象。
上代码

还是和上面一样 定义 产品

public abstract class Game {
    abstract void play();

    public void start() {
        System.out.println("开始游戏");
    }

    public void end() {
        System.out.println("结束游戏");
    }

}

具体产品

public class WoWGame extends Game{
    @Override
    void play() {
        System.out.println("魔兽世界启动");
    }
}

public class CFGame extends Game{
    @Override
    void play() {
        System.out.println("来吧,辽一运输船中门对狙");
    }
}

 定义工厂接口

public interface GameFactoryMethod {
    Game createGame();
}

定义两个具体工厂实现工厂接口

public class CFGameFactoryMethod implements GameMethodFactory{
    @Override
    public Game createGame() {
        return new CFGame();
    }
}

public class WoWGameFactoryMethod implements GameMethodFactory{
    @Override
    public Game createGame() {
        return new WoWGame();
    }
}

还是main方法

public class Client {
    public static void main(String[] args) {

        GameFactoryMethod factory = new CFGameFactoryMethod();
//        GameFactoryMethod factory = new WoWGameFactoryMethod();
        Game game = factory.createGame();
        game.start();
        game.play();
        game.end();
    }
}

相对于上面的简单工厂 工厂方法在添加新的游戏时 只需要去实现工厂接口 而不需要修改原有的代码 并增加接口和客户端的耦合度 降低 具体实现和客户端的耦合度 实现了开闭原则和依赖倒转原则

设计模式 6大原则之 :开闭原则,依赖倒转原则-CSDN博客

显而易见它的优点 :

将对象创建和使用进行解耦

在添加新的产品时 不需要修改原来的代码 具体商品去继承抽象商品类 商品工厂去实现工厂接口

但他也有明显的缺点上述代码 两款游戏都有它不同的 具体产品和具体工厂 那么再添加一款呢 原神启动 是不是也需要添加原神来继承产品并创建原神工厂类来实现抽象工厂接口 ?所以他的缺点:

每增加一个新的产品就需要加一个产品和产品工厂 增加了系统复杂度

适用场景

  1. 创建对象的具体类不是在编译时确定的: 如果系统中的对象类型在编译时无法确定,而是在运行时根据某些条件决定的话,工厂方法模式就显得很合适。每个具体工厂负责创建特定类型的对象,根据运行时条件选择合适的具体工厂。

  2. 一个类无法预知它所需要的对象的类: 当一个类无法预知它所需要的具体对象的类,但只知道它需要一些对象,并且这些对象应该符合某个接口或抽象类时,工厂方法模式能够提供一种灵活的解决方案。

  3. 希望用户可以扩展框架的时候: 工厂方法模式允许用户通过添加新的具体工厂和产品类来扩展框架,而无需修改已有的代码。这使得系统更容易扩展和维护。

  4. 遵循单一职责原则: 工厂方法模式遵循单一职责原则,每个具体工厂负责创建特定类型的对象。这有助于代码的组织和维护,使得每个类都有一个明确的职责。

举个例子:假如说有一个上传文件的接口 他可以保存到数据库 也可以保存二进制文件 类似这种 后期可以实现保存成word或者Excel 只需要继承或实现 文件处理对象(接口)并添加 对应的工厂 这种就十分符合这个工厂方法(只做举例 因为实在想不出更好的例子了 我也没接触到过适用的场景 我举例这种更适合 策略模式{后续会更} 因为处理文件更主要的是处理的细节而不是返回什么对象)

接下来重头戏

抽象工厂

概念

它提供了一个接口用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。抽象工厂模式是工厂方法模式的升级版本,它不仅提供了创建产品的接口,还提供了创建产品族的接口

抽象工厂在上述工厂方法基础上又多了两个概念

产品族和产品等级。

  1. 产品族(Product Family):

    产品族是指一组相关或相互依赖的产品集合,这些产品通常共同用于完成某个整体的功能。在抽象工厂模式中,一个具体的工厂负责创建一个产品族。产品族中的产品可以彼此关联,它们通常共同用于满足一个整体需求。
  2. 产品等级(Product Level):

    产品等级是指一组具有相同用途或抽象性质的产品集合。在抽象工厂模式中,产品等级是由抽象产品接口(或抽象类)定义的,具体工厂负责创建属于同一产品等级的具体产品。

 产品等级:上述代码 魔兽世界和穿越火线 都是电脑游戏 那么他们就是一个产品等级

但是 魔兽在中国是网易代理的 穿越火线是腾讯 那么 网易云音乐和魔兽世界就是一个产品族 穿越火线和qq音乐就是一个产品族 同样的 网易云音乐和qq音乐也是一个产品等级 类图就不画了自行脑补....

上代码

首先定义产品抽象类 即为 游戏和音乐(产品等级)

public abstract class Game {
    abstract void play();

    public void start() {
        System.out.println("开始游戏");
    }

    public void end() {
        System.out.println("结束游戏");
    }

}

public abstract class Music {

    public abstract void listen();
}

具体产品

public class WoWGame extends Game {
    @Override
    void play() {
        System.out.println("魔兽世界启动");
    }
}
public class CloudMusic extends Music {
    @Override
    public void listen() {
        System.out.println("网易云音乐开启");
    }
}
public class CFGame extends Game {
    @Override
    void play() {
        System.out.println("来吧,辽一运输船中门对狙");
    }
}
public class QQMusic extends Music{
    @Override
    public void listen() {
        System.out.println("QQ音乐,用我我有周杰伦!");
    }
}

定义工厂接口

public interface SoftwareAbstractFactory {

    Music createMusic();

    Game createGame();
}

定义具体工厂 (产品族)

public class TencentSoftwareFactory implements SoftwareAbstractFactory{
    @Override
    public Music createMusic() {
        return new QQMusic();
    }

    @Override
    public Game createGame() {
        return new CFGame();
    }
}

public class NTESSoftwareFactory implements SoftwareAbstractFactory {
    @Override
    public Music createMusic() {
        return new CloudMusic();
    }

    @Override
    public Game createGame() {
        return new WoWGame();
    }
}

模拟客户端

public class Client {
    public static void main(String[] args) {
//        SoftwareAbstractFactory factory = new NTESSoftwareFactory();
        SoftwareAbstractFactory factory = new TencentSoftwareFactory();
        factory.createMusic().listen();
        factory.createGame().play();
    }
}

在上述实现了抽象方法代码中 他的缺点也显而易见 因为用到了大量接口 那么如果产品族添加一个产品等级 那么就需要改原有的接口 那么所有的实现类都需要改 就好比说 上面是网易和腾旭的电脑游戏和听歌软件 那么手机游戏呢?网易有明日之后 腾讯金铲铲 这样就需要有大量改动 不画图 自行脑补.... 也就是:当产品族需要增加一个新的产品时,所有的工厂类都需要进行修改

当然 它的优点:

提供了一种封装对象创建过程的方式:抽象工厂模式将对象的创建过程封装在了具体的工厂类中,客户端只需要关注工厂接口和产品接口,无需关心具体的实现细节。
实现了产品族的切换:通过使用不同的具体工厂类,可以方便地切换整个产品族的产品,从而实现了接口与实现的分离。
保持了代码的一致性:抽象工厂模式保持了一致性,即所有由同一工厂创建的产品都相互关联,使得产品之间可以很方便地进行配合使用。

感觉说的不是特别好 感觉没理解参考这位大佬的博客:抽象工厂模式(通俗易懂)_抽象工厂 通俗的方式-CSDN博客

总结

根据自己的实际情况去选择适合自己的 通常会选择简单静态工厂 其实 还有一个模拟spring ioc的一个代码 但是帖子太长 不爱看 (好吧我写着也累)所以以后会出一个所有设计模式代码的总结 主要还是参考 大佬的思维或比较优秀框架的 大家一起学习 多多进步 加油!!

  • 45
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值