Java设计模式-创建者模式-工厂模式-简单工厂模式(不属于23种设计模式之一)

岳庆锦

发布于 2022.03.14 20:56 阅读 835 评论 0

工厂模式

 定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子类工厂类中实现。(这也满足创建型模式的特点“创建与使用分离”

 分类:按实际业务场景划分,工厂模式可分为3类:

 ①简单工厂模式(不属于GOF 23种设计模式之一)

 ②工厂方法模式

 ③抽象工厂模式

 应用场景:在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式。

 复杂对象:类的构造函数参数过多等对类的构造有影响的情况。因为类的构造过于复杂的话,如果直接在其他业务类内使用,则两者会耦合过重。后续业务更改,就需要在任何引用该类的源代码内进行更改,这会影响软件开发的效率。

 下面的案例中会体现创建复杂对象时,不用工厂模式造成的耦合过重的情况。

简单工厂模式(静态工厂方法模式)

 在介绍简单工厂模式之前,我们先来弄清楚工厂模式中提到的“产品”和“工厂”的概念。

 产品:被创建的对象。

 工厂:创建产品的对象。

 定义:创建的产品不多,只要一个工厂类就可以。这种模式叫做“简单工厂模式”。

 简单工厂模式中创建实例的方法通常为静态(static)方法,因此“简单工厂模式”又叫做“静态工厂方法模式”。

 结构

 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用(因为是静态的方法),创建所需的产品对象。(简单来说就是,该类提供了创建产品的方法,调用者通过该方法来创建具体产品)

 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。(定义产品的规范,描述了产品的主要特性和功能)

 具体产品(ConcreteProduct):是简单工厂模式的创建目标。(实现或者继承抽象产品的子类)

 应用:下面以“咖啡店”为案例,理解简单工厂模式。

 分析:此案例中有咖啡类(Coffee),其中,美式咖啡(AmericanCoffee)和拿铁咖啡(LatteCoffee)作为咖啡店类的子类,继承咖啡类。咖啡店类(CoffeStore)包含点咖啡的方法。

 我们先不用简单工厂模式来实现此案例,具体类图和代码如下:

1.咖啡类(Coffee),包含获取具体咖啡名称的抽象方法和加糖、加奶的具体方法。(所有咖啡都要加糖、加奶,但咖啡名不一样,要求子类重写)

public abstract class Coffee {

    //获取咖啡名称
    public abstract String getName();

    //加糖
    public void addSugar(){
        System.out.println("加糖");
    }

    //加奶
    public void addMilk(){
        System.out.println("加奶");
    }
}

2.美式咖啡类(AmericanCoffee),实现咖啡类(Coffee),重写父类中获取咖啡名的方法。

public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}

3.拿铁咖啡类(LatteCoffee)

public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

4.咖啡店类(CoffeeStore),包含点咖啡的方法(orderCoffee(String type)),参数是字符串,传入咖啡类型。

public class CoffeeStore {

    public Coffee orderCoffee(String type){

        //声明coffee类型的变量,根据不同类型创建不同的coffee类对象
        Coffee coffee = null;

        if("american".equals(type)){
            coffee = new AmericanCoffee();
        }else if ("latte".equals(type)){
            coffee = new LatteCoffee();
        }else {
            //抛出运行时异常
            throw new RuntimeException("对不起,您所点的咖啡没有");
        }

        //加配料,加糖,加奶
        coffee.addMilk();
        coffee.addSugar();

        //制作好了,返回咖啡对象
        return coffee;
    }
}

5.测试类(Client)

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

        //创建咖啡店类
        CoffeeStore store = new CoffeeStore();
        //制作咖啡
        Coffee coffee = store.orderCoffee("latte");

        System.out.println(coffee.getName());
    }
}

6.运行结果

 代码分析:上面咖啡店类(Coffee)通过点咖啡功能创建咖啡对象的过程是比较复杂的,而且咖啡店类和具体咖啡类产生了耦合,后期如果添加一种新类型的咖啡,那么所有new咖啡对象的地方都要修改代码,这降低了开发效率而且违背了“开闭原则”

 后面介绍的简单工厂模式其实也违背了“开闭原则”,但在客户端(比如本例中的咖啡店类)比较多的情况下,当需求发生变化时,不用简单工厂模式的话,需要逐一修改客户端的代码;而在使用简单工厂模式的情况下,只需修改工厂类的代码。我们可以通过改进后的代码来体会这一点。

 改进方法:使用简单工厂模式。由原来业务逻辑在咖啡店里实现,变为业务逻辑在工厂类中实现。客户端只需要传入工厂类的参数(在本案例中就是咖啡名称),不需要关心创建对象的逻辑。

  在改进后的代码中,咖啡类(Coffee)、美式咖啡(American)、拿铁咖啡(LatteCoffee)和测试类(Client)中的代码和运行结果不发生变化,新创建了简单工厂类(SimpleCoffeeFactory),咖啡店类(CoffeeStore)中的代码发生变化。

 这里我们就只展示简单工厂类和咖啡店类的代码。

1.简单工厂类(SimpleCoffeeFactory),包含制造咖啡的静态方法,可以被外界(CoffeeStore)直接调用,传入的参数是字符串类型,代表咖啡类型。

public class SimpleCoffeeFactory {

    //制造咖啡的方法,通过字符串判断具体制造哪种类型的咖啡
    //方法设置为静态的,以便创建产品类的方法可以被外界直接调用
    public static Coffee creatCoffee(String type){
        //声明coffee类型的变量,根据不同类型创建不同的coffee类对象
        Coffee coffee = null;
        if("american".equals(type)){
            coffee = new AmericanCoffee();
        }else if ("latte".equals(type)){
            coffee = new LatteCoffee();
        }else {
            //抛出运行时异常
            throw new RuntimeException("对不起,您所点的咖啡没有");
        }
        return coffee;
    }
}

2.咖啡店类(CoffeeStore)

public class CoffeeStore {

    public Coffee orderCoffee(String type){
        //调用生产咖啡的方法,此时咖啡店也不依赖于具体的产品对象了
        Coffee coffee = SimpleCoffeeFactory.creatCoffee(type);

        //加配料,加糖,加奶
        coffee.addMilk();
        coffee.addSugar();

        return coffee;
    }
}

 改进后代码分析:①把创建具体咖啡的业务逻辑放到了工厂类中。②咖啡店中的点咖啡方法(orderCoffee())变成了工厂的客户,咖啡对象直接从工厂中获取。③解除了咖啡店类和咖啡类的耦合,但也产生了新的耦合(工厂类和咖啡类的耦合)。④后期需求发生改变,只需更改工厂类的代码。

 简单工厂模式的优点

 封装了创建对象的过程,可以通过工厂类中的静态方法直接获取对象,把对象(咖啡)业务逻辑层(咖啡店点咖啡)分开,如果要实现新产品就只需修改工厂类,降低了客户代码修改的可能性,更加容易扩展。

 缺点:增加新产品要修改工厂类代码,违背了“开闭原则”。

 应用场景:对于产品种类相对较少的情况,可以使用简单工厂模式。