Fork me on GitHub

简单工厂模式和工厂方法模式

文章首发于我的个人博客,欢迎访问:https://blog.itzhouq.cn/factory

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 Java里边共有23种设计模式而工厂模式就有三种,它们分别是简单工厂模式(并不在23中模式之中),工厂方法模式以及抽象工厂模式,其中我们通常所说的工厂模式指的是工厂方法模式,工厂方法模式是日常开发中使用频率最高的一种设计模式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

1、介绍

意图: 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决: 主要解决接口选择的问题。

何时使用: 我们明确地计划不同条件下创建不同实例时。

如何解决: 让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码: 创建过程在其子类执行。

应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点: 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

注意事项: 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

2、简单工厂模式

简单工厂模式实现

使用简单的例子说明什么是简单工厂模式。

假设现在有不同种类的车,消费者可以自行选择消费某种车。如果按照以前的思路是将车作为一个接口,不同的车实现这个接口,消费时,创建一个实现类,将车接口指向实现类。代码如下:

创建一个车的接口:

public interface Car {
    void getName();
}

创建一个实现类,比如五菱宏光

public class Wuling implements Car {
    @Override
    public void getName() {
        System.out.println("五菱宏光");
    }
}

再创建一个实现类,比如特斯拉

public class Tesla implements Car {
    @Override
    public void getName() {
        System.out.println("特斯拉");
    }
}

现在模拟消费车的过程,比如我们需要买一辆五菱宏光,再买一辆特斯拉,来一个测试类:

public class FactoryTest {
    public static void main(String[] args) {
        Car car = new Wuling();
        Car car2 = new Tesla();

        car.getName(); // 五菱宏光
        car2.getName(); // 特斯拉
    }
}

可以看到我们每次消费车的时候都需要使用new关键词来创建车对象。这其实是有问题。对于消费者来说,我不关心车是怎么造出来的,我只需要知道车的名称就好了,但是这里消费者需要知道车的结构,创建车对象需要哪些参数。这里的车对象创建的时候使用了无参构造器,实际的情况可能复杂一些。

为了解决这个问题我们可以引入一个工厂类,这个工厂类目的是制造各种车。消费者消费的时候直接根据车的名称从工厂中拿即可,不需要知道车的结构,不需要自己new对象。

public class CarFactory {
    public static Car getCar(String car) {
        if ("五菱宏光".equalsIgnoreCase(car)) {
            return new Wuling();
        } else if ("特斯拉".equalsIgnoreCase(car)) {
            return new Tesla();
        } else {
            return null;
        }
    }
}

现在再次模拟消费的过程:

public class FactoryTest {
    public static void main(String[] args) {
//        Car car = new Wuling();
//        Car car2 = new Tesla();
        // 使用工厂创建
        Car car = CarFactory.getCar("五菱宏光");
        Car car2 = CarFactory.getCar("特斯拉");

        car.getName(); // 五菱宏光
        car2.getName(); // 特斯拉
    }
}

这样简单工厂模式就实现了。

简单工厂模式的核心本质:

  • 实例化对象不需要new,用工程方法代替
  • 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

简单工厂模式的问题

现在有一个需求:消费者需要购买大众牌的车。对于这个需求,我们需要修改新引入一个类Dazhong,实现Car接口,然后修改工厂类。

public class Dazhong implements Car {
    @Override
    public void getName() {
        System.out.println("大众");
    }
}

修改工厂:

public class CarFactory {
    public static Car getCar(String car) {
        if ("五菱宏光".equalsIgnoreCase(car)) {
            return new Wuling();
        } else if ("特斯拉".equalsIgnoreCase(car)) {
            return new Tesla();
        } else if ("大众".equalsIgnoreCase(car)) {
            return new Dazhong();
        } else {
            return null;
        }
    }
}

添加了一个新的判断。消费的方式没有变化。

从设计的角度讲,简单工厂模式是不符合开闭原则。增加了一个新的产品,需要修改原来的代码。所以简单工厂模式也叫静态工厂模式

下面使用示意图分析一下简单工厂模式。

简单工厂模式

为了解决简单工程模式的问题,我们引入工厂方法模式。

3、工厂方法模式

工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。

车接口不变:

public interface Car {
    void getName();
}

引入一个车的工厂接口,所有具体的车工厂都要实现这个接口:

public interface CarFactory {
    Car getCar();
}

五菱宏光:五菱宏光车工厂和实现类:

public class WulingFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new Wuling();
    }
}
public class Wuling implements Car {
    @Override
    public void getName() {
        System.out.println("五菱宏光");
    }
}

特斯拉:特斯拉工厂和实现类:

public class TeslaFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new Tesla();
    }
}
public class Tesla implements Car {
    @Override
    public void getName() {
        System.out.println("特斯拉");
    }
}

模拟消费:

public class Consumer {
    public static void main(String[] args) {
        Car car = new WulingFactory().getCar();
        Car car2 = new TeslaFactory().getCar();

        car.getName(); // 五菱宏光
        car2.getName(); // 特斯拉
    }
}

这就是工程方法模式,符合开闭原则。

对于需求,不需要修改现有的工厂类,而是增加一个对应的车工厂和实现类。比如,现在增加了一个摩拜车。

那我先写一个摩拜车类:

public class Mobai implements Car {
    @Override
    public void getName() {
        System.out.println("摩拜");
    }
}

再写一个摩拜车工厂:

public class MobaiFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new Mobai();
    }
}

消费者消费这个车的车的时候,不需要修改CarFactory,直接使用摩拜车工厂创建摩拜车。

public class Consumer {
    public static void main(String[] args) {
        Car car = new WulingFactory().getCar();
        Car car2 = new TeslaFactory().getCar();
        Car car3 = new MobaiFactory().getCar();

        car.getName(); // 五菱宏光
        car2.getName(); // 特斯拉
        car3.getName(); // 摩拜
    }
}

和简单工厂对比一下,最根本的区别在于,简单工厂只有一个统一的工厂类,而工厂方法是针对每个要创建的对象都会提供一个工厂类,这些工厂类都实现了一个工厂基类(本例中的CarFactory )。

示意图:

工厂方法模式

4、比较简单工厂模式和工厂方法模式

从设计原则比如开闭原则来看,工厂方法模式更好。但是从结构复杂度、代码复杂度、编程复杂度、管理复杂度上来说,简单工厂模式更方便,实际使用得更多。

5、工厂模式的应用场景

  • JDK 中 Calendar 的 getInstance 方法
  • JDBC 中 Connection 对象的获取
  • Spring 中的 IOC 容器创建管理 bean对象
  • 反射中 Class对象的 newInstance 方法。

参考文章1

参考文章2

posted @ 2020-04-21 14:00  itzhouq  阅读(859)  评论(0编辑  收藏  举报