什么是工厂模式?以及三种工厂模式的区别和特点

简介

工厂模式(Factory Pattern)是Java中一种常见的设计模式,属于创建型模式的一种。它用于创建对象,但与直接在代码中使用new关键字创建对象不同,工厂模式通过一个共同的接口来创建对象,从而将对象的创建过程与具体的使用客户端代码分离开来。

工厂模式通常涉及以下几个角色:

  1. 抽象工厂(Abstract Factory):定义一个创建对象的接口,但不负责具体的对象创建过程。它通常是一个接口或者抽象类,其中定义了一个或多个创建对象的方法。

  2. 具体工厂(Concrete Factory):实现抽象工厂接口,负责实际创建具体的对象。每个具体工厂都对应着一种具体的对象类型。

  3. 产品(Product):工厂所创建的对象类型。它可以是一个接口、抽象类或者具体类。

  4. 具体产品(Concrete Product):实现产品接口的具体对象。工厂模式的每个具体工厂都负责创建特定的具体产品。

工厂模式的主要目的是将对象的创建过程封装在工厂类中,客户端代码只需要关心从工厂获取对象的过程,而不需要了解对象的创建细节。这样可以降低代码的耦合度,提高代码的可维护性和可扩展性

工厂模式有三种,分别是简单工厂模式、工厂方法模式、抽象工厂模式。三种模式从前到后越来越抽象,也更具有一般性

场景

从前,在一个小镇上,有一家家庭面馆。这家面馆以制作各种口味的面条而闻名,包括牛肉面、鸡肉面和素面等等。老板娘是个聪明的女人,她发现每天都有很多顾客来点不同口味的面条,但是她却发现每次都要等很长时间才能做好。

为了提高生产效率,老板娘决定引入简单工厂模式。她聘请了一位厨师长,并设置了一个独立的厨房区域。在厨房里,她放置了三个不同的面条制作区,分别用来制作牛肉、鸡肉和素面。

每当有顾客来点菜,服务员会将订单传递给老板娘。老板娘会根据订单的要求选择相应的面条种类,并通知厨师长。厨师长会将订单送到相应的面条制作区,厨师们会根据订单要求制作出对应口味的面条。

这样一来,无论顾客点的是哪种口味的面条,都可以很快地制作出来。顾客不再需要等待很长时间,而且面馆的运营效率也得到了极大的提高。

通过引入简单工厂模式,这家面馆成功地解决了之前生产效率低下的问题,使得顾客能够更快地享用到美味的面条。

if-else实现

    @Test
    public void ifElseTest(){
        String type = "chicken";
        if ("chicken".equals(type)) {
            System.out.println("鸡肉面");
        } else if ("beef".equals(type)) {
            System.out.println("牛肉面");
        } else if ("vegetarian".equals(type)) {
            System.out.println("素面");
        } else {
            System.out.println("面条不存在");
        }
    }

通过示例代码发现,if-else语句需要对每个条件进行显式编写,如果后续条件多了会导致代码冗余过长,并且不利于扩展和维护

简单工厂模式

策略模式实现

public interface Noodle {

    /**
     *  制作接口类
     * @author yiridancan
     **/
    void cook();

}
/**
 * 牛肉面实现类
 * @author yiridancan
 **/
public class BeefNoodle implements Noodle{
    @Override
    public void cook() {
        System.out.println("牛肉面正在准备中...");
    }
}



/**
 * 鸡肉面实现类
 * @author yiridancan
 **/
public class ChickenNoodle implements Noodle{
    @Override
    public void cook() {
        System.out.println("鸡肉面正在准备中...");
    }
}


/**
 * 素面实现类
 * @author yiridancan
 **/
public class VegetarianNoodle implements Noodle{
    @Override
    public void cook() {
        System.out.println("素面正在准备中...");
    }
}
/**
 * 简单工厂类
 * @author yiridancan
 **/
public class NoodleFactory {

    public static Noodle getNoodle(String type){
        switch (type){
            case "beef":
                return new BeefNoodle();
            case "chicken":
                return new ChickenNoodle();
            case "vegetarian":
                return new VegetarianNoodle();
            default:
                return null;
        }
    }

}
    @Test
    public void factoryTest(){
        //鸡肉面
        Noodle chicken = NoodleFactory.getNoodle("chicken");
        chicken.cook();

        //牛肉面
        Noodle beef = NoodleFactory.getNoodle("beef");
        beef.cook();

        //素面
        Noodle vegetarian = NoodleFactory.getNoodle("vegetarian");
        vegetarian.cook();
    }

首先定义了一个公共的Noodle抽象类,然后定义了BeefNoodle、ChickenNoodle、VegetarianNoodle三个策略类,它们分别是牛肉面、鸡肉面、素面,都实现了Noodle类的cook接口

接着定义了一个NoodleFactory简单工厂类,其中有一个静态方法getNoodle,根据传入的参数类型创建相对于的策略类

最后,在测试代码中,通过调用简单工厂的 getNoodle方法来创建不同类型的策略类,并调用其 cook 方法来制作面条。

简单工厂的缺点

  1. 违反开放-封闭原则(OCP): 当需要新增一种产品时,需要修改工厂类的源代码来添加新的产品类型,这违反了开放-封闭原则。每次修改都会导致工厂类的变化,可能引发其他代码的修改和重新测试,增加了维护成本和风险。

  2. 单一职责原则问题: 简单工厂模式中的工厂类负责创建不同类型的产品,导致工厂类的职责不够单一,违反了单一职责原则。随着产品类型的增加,工厂类的代码逐渐变得臃肿,难以维护和理解。

  3. 不易扩展: 如果需要新增一种产品类型,除了要修改工厂类的代码外,还可能需要修改客户端代码来传递正确的参数类型。这种做法不够灵活,难以应对产品类型的频繁变化。

  4. 隐藏了产品的创建细节: 虽然简单工厂模式封装了对象的创建过程,但它也隐藏了产品的具体创建细节,导致客户端无法直接控制对象的创建过程,无法灵活地定制对象的创建方式。

综上所述,简单工厂模式虽然简单易懂,但在面对复杂的需求变化时,其不足之处会逐渐显现出来,可能不够灵活和可维护。因此,在某些情况下,可以考虑使用其他创建型设计模式,如工厂方法模式或抽象工厂模式,以更好地应对系统的变化和扩展

工厂方法模式

定义抽象接口:

public interface Noodle {

    /**
     *  制作接口类
     * @author yiridancan
     **/
    void cook();

}


public interface NoodleFactory {

    /**
     *  面条工厂接口
     * @author yiridancan
     **/
    Noodle createNoodle();
}

创建具体的面条类型工厂:

/**
 *  具体的工厂类:牛肉面工厂
 * @author yiridancan
 **/
public class BeefNoodleFactory implements NoodleFactory{
    @Override
    public Noodle createNoodle() {
        return new BeefNoodle();
    }
}


/**
 *  具体的工厂类:鸡肉面工厂
 * @author yiridancan
 **/
public class ChickenNoodleFactory implements NoodleFactory{
    @Override
    public Noodle createNoodle() {
        return new ChickenNoodle();
    }
}


/**
 *  具体的工厂类:素面工厂
 * @author yiridancan
 **/
public class VegetarianNoodleFactory implements NoodleFactory{
    @Override
    public Noodle createNoodle() {
        return new VegetarianNoodle();
    }
}

编写测试类:

    @Test
    public void factoryTest(){
        // 创建牛肉面工厂,并制作牛肉面
        NoodleFactory beefNoodleFactory = new BeefNoodleFactory();
        Noodle beefNoodle = beefNoodleFactory.createNoodle();
        beefNoodle.cook();

        // 创建鸡肉面工厂,并制作鸡肉面
        NoodleFactory chickenNoodleFactory = new ChickenNoodleFactory();
        Noodle chickenNoodle = chickenNoodleFactory.createNoodle();
        chickenNoodle.cook();

        // 创建素面工厂,并制作素面
        NoodleFactory vegetarianNoodleFactory = new VegetarianNoodleFactory();
        Noodle vegetarianNoodle = vegetarianNoodleFactory.createNoodle();
        vegetarianNoodle.cook();
    }

相较于简单工厂模式,工厂方法模式摒弃了在工厂类内部显式地运用条件判断来指向具体类的设计方式,转而只需通过新增相应的工厂类来轻松实现功能扩展,从而显著增强了我们代码的可扩展性和可维护性。然而,在面对大量类型需求时,可能会导致工厂类数量的增长与膨胀。

抽象工厂模式

先定义面条、饮料抽象工厂类:

/**
 * 面条接口类
 * @author yiridancan
 **/
public interface Noodle {

    void cook();
}


/**
 * 饮料接口类
 * @author yiridancan
 **/
public interface Beverage {

    void brew();
}

然后根据抽象工厂类实现具体的产品类型:

/**
 * 定义具体的产品:牛肉面
 * @author yiridancan
 **/
public class BeefNoodle implements Noodle {
    @Override
    public void cook() {
        System.out.println("牛肉面正在准备中...");
    }
}


/**
 * 定义具体的产品:意大利面
 * @author yiridancan
 **/
public class ItalianNoodle implements Noodle {
    @Override
    public void cook() {
        System.out.println("意大利面正在准备中...");
    }
}

/**
 * 定义具体的产品:茶
 * @author yiridancan
 **/
public class Tea implements Beverage {
    @Override
    public void brew() {
        System.out.println("茶正在准备中...");
    }
}

/**
 * 定义具体的产品:咖啡
 * @author yiridancan
 **/
public class Coffee implements Beverage {
    @Override
    public void brew() {
        System.out.println("咖啡正在准备中...");
    }
}

定义一个具体的工厂类,用来实现组合套餐

/**
 * 定义抽象工厂,用来组合套餐
 * @author yiridancan
 **/
public interface FoodFactory {

    /**
     * 创建面条
     * @author yiridancan
     * @return Noodle
     **/
    Noodle createNoodle();

    /**
     * 创建饮料
     * @author yiridancan
     * @return Beverage
     **/
    Beverage createBeverage();
}

西餐厅:意大利面搭配咖啡

/**
 * 定义具体工厂:西餐厅,包含咖啡喝意大利面
 * @author yiridancan
 **/
public class WesternFoodFactory implements FoodFactory {
    @Override
    public Noodle createNoodle() {
        return new ItalianNoodle();
    }

    @Override
    public Beverage createBeverage() {
        return new Coffee();
    }
}

中餐厅:牛肉面搭配茶

/**
 * 定义具体工厂:中餐厅,包含牛肉面和茶
 * @author yiridancan
 **/
public class ChineseFoodFactory implements FoodFactory {
    @Override
    public Noodle createNoodle() {
        return new BeefNoodle();
    }

    @Override
    public Beverage createBeverage() {
        return new Tea();
    }
}

编写测试类:

    @Test
    public void factoryTest(){
        //西餐厅
        FoodFactory westernFood = new WesternFoodFactory();
        westernFood.createNoodle().cook();
        westernFood.createBeverage().brew();
        System.out.println("==============================");
        //中餐厅
        FoodFactory chineseFood = new ChineseFoodFactory();
        chineseFood.createNoodle().cook();
        chineseFood.createBeverage().brew();
    }

上述代码展示了如何根据不同设计模式创建不同类型的面条。在抽象工厂模式中,我们不仅创建面条,还创建与其搭配的汤品,体现了同时生产相关产品的概念

总结

  1. 简单工厂模式 (Simple Factory Pattern)

    • 核心: 定义了一个工厂类,它包含创建一系列相关或依赖对象的方法。这个工厂类负责决定应该实例化哪一个具体类。
    • 特点:
      • 工厂类承担了全部的实例化逻辑,客户端直接调用工厂类的方法来获取产品对象。
      • 当增加新的产品类型时,通常需要修改工厂类的代码以添加新产品的创建逻辑,违反了“开闭原则”(Open/Closed Principle)。
    • 适用场景: 创建对象的逻辑相对简单,产品种类较少且变化不频繁的情况。
  2. 工厂方法模式 (Factory Method Pattern)

    • 核心: 定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法是在抽象类中声明的,由其子类实现,用来创建所需的产品对象
    • 特点:
      • 抽象工厂类提供了创建产品的接口(工厂方法),由子类来决定如何实例化实际的产品对象
      • 符合“开闭原则”,增加新的产品类型时只需增加一个新的子类,而不需要修改现有的工厂类代码
      • 通过引入继承结构来实现产品对象的创建,支持更多样化的创建逻辑
    • 适用场景: 需要根据不同条件创建不同产品对象,且产品对象具有一定的扩展可能性
  3. 抽象工厂模式 (Abstract Factory Pattern)

    • 核心: 提供一个接口,用于创建相关或互相依赖对象家族的一个完整系列,而不只是单个对象。它允许客户端使用抽象接口来创建一整套相关的对象,而不必指定具体类
    • 特点:
      • 抽象工厂不只是创建单个对象,而是创建一系列相关的产品对象(称为产品族),这些产品之间存在某种关系或依赖性
      • 客户端通过调用抽象工厂的不同方法来获取不同种类的产品对象,而无需关心具体的产品实现
      • 更高层次的抽象,适合处理产品间的复杂依赖关系,以及应对产品结构的变化。
      • 同样遵循“开闭原则”,增加新的产品族时,需要增加新的抽象工厂子类
    • 适用场景: 系统中有多组相关的产品对象,而且客户端需要消费这一系列产品的时候,或者希望系统独立于这些产品的具体实现细节

总结来说:

  • 简单工厂模式最简单,集中控制所有对象的创建,但扩展性较差
  • 工厂方法模式通过继承将对象创建的责任分散到各个子类中,提高了扩展性
  • 抽象工厂模式进一步抽象,关注于创建一系列相关对象,特别适合处理具有多种产品族的情况,其复用性和扩展性更强
  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值