设计模式(二)简单工厂模式

一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。不过,在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以工厂模式只被分成了工厂方法和抽象工厂两类。实际上,前面一种分类方法更加常见,所以,在今天的讲解中,我们沿用第一种分类方法。

简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。所以,我们今天只讲解前两种工厂模式。

简单工厂(Simple Factory)

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

简而言之就是在使用一个具体对象的时候,我们不直接new一个对象,而是通过一个单独的工厂类来new这个对象。

简单工厂模式的结构图如下所示:

设计模式(二)简单工厂模式

  • Factory:工厂类,负责实现创建所有实例的内部逻辑。创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • IProduct:抽象产品类,这是简单工厂模式所创建的所有对象的父类。
  • Product:具体产品类,继承自抽象产品类。

我们以生产手机为例,用代码来实现一下:

(1)抽象产品类

创建一个手机的抽象产品类,其有一个抽象方法用于启动手机:

abstract class Phone{
    public abstract void start();
}

(2)具体产品类

我们创建各种品牌的手机,他们都继承自父类Phone,并实现了启动的方法。具体分为oppo手机,vivo手机和小米手机:

class OppoPhone extends Phone {
    @Override
    public void start() {
        System.out.println("OPPO start");
    }
}

class VivoPhone extends Phone {
    @Override
    public void start() {
        System.out.println("VIVO start");
    }
}
class XiaomiPhone extends Phone {
    @Override
    public void start() {
        System.out.println("XiaoMi start");
    }
}

(3)工厂类

我们创建一个手机工厂,用来生产各种手机:

class PhoneFactory{
    public static Phone createPhone(String type) {
        Phone phone = null;
        switch (type) {
            case "oppo":
                phone = new OppoPhone();
                break;
            case "vivo":
                phone = new VivoPhone();
                break;
            case "xiaomi":
                phone = new XiaomiPhone();
                break;
        }
        return phone;
    }
}

(4)调用工厂类生产手机并启动

    public static void main(String[] args) {
        PhoneFactory.createPhone("oppo").start();
        PhoneFactory.createPhone("vivo").start();
        PhoneFactory.createPhone("xiaomi").start();
    }

设计模式(二)简单工厂模式

打印日志可以看到这种品牌的手机都启动了。至此,我们就完成了简单工厂模式的创建。

问题

简单工厂模式的问题在于如果后续有其他品牌的手机需要生产,那我们就要修改工厂的if-else语句,如果逻辑简单还好,但在实际开发中,if-else的代码块逻辑可能很复杂,稍有疏忽就容易制造出成吨的bug。那如何解决这样的问题呢?我们可以利用泛型和反射来优化我们的简单工厂。

解决

对于普通的简单工厂,需要维护好传入的生产需求和具体生产的对象的关系,根据需求生产具体产品。有没有什么一劳永逸的方法呢?当然有,我们可以创建一个万能的自动化工厂,传入什么类型的产品,工厂就生产什么样的产品。工厂内部的逻辑不需要根据传入的产品变更而更改。具体实现如下:

class AutoPhoneFactory {
    public static  <T extends Phone> T createPhone(Class<T> clazz) {
        Phone phone = null;
        try {
            phone = (Phone) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) phone;
    }
}

main方法中调用:

    public static void main(String[] args) {
        AutoPhoneFactory.createPhone(OppoPhone.class).start();
        AutoPhoneFactory.createPhone(VivoPhone.class).start();
        AutoPhoneFactory.createPhone(XiaomiPhone.class).start();
    }

设计模式(二)简单工厂模式
从日志可以看到各种品牌的手机同样是启动了,而且对于新增的品牌,我们无需修改工厂类,只需传入新增品牌的.class就可以创建对应的实例对象了。

总结

使用场景

  • 工厂类负责创建的对象较少;
  • 客户只需要知道传入工厂类的参数,而无需关心创建对象的逻辑。

优点

  • 使用户根据参数获得对应的类实例,避免直接实例化类,降低了耦合性。

缺点

  • 可实例化的类型在编译期间已经被确定。如果增加新类型,需要修改工厂,违背了开放封闭原则;
  • 使用简单工厂需要知道所要生成的类型,当子类过多或子类层次过多时不适合使用简单工厂模式;
  • 对于使用泛型和反射优化的简单工厂类,因为使用了泛型创建实例,其性能有一定的问题,所以虽然做到了新增产品类不用修改工厂,但是在大量创建实例时,性能有一定损耗。

示例代码已上传Github

学习更多知识,请关注我的个人博客:droidYu

上一篇:剑指offer_015 字符串中的所有变位词


下一篇:015.SpringBoot配置文件(Resource目录下)