当需要创建的对象他有非常非常多的属性需要设置时,一个个set显然比较麻烦
而工厂模式可根据不同的业务场景将需要设置的属性封装成一个个方法,一劳永逸

工厂模型核心是生产,用户,销售分开,这样可以降低耦合,处理高并发情况下导致的仓库里没东西了还继续售卖的情况(超卖),对电商项目很有用

简单工厂模式

  • 简单工厂模式属于创建型模式,工厂模式的一种,由一个工厂对象决定创建出哪一种产品类的实例
  • 定义一个创建对象的类,由这个类来封装实例化对象的行为
  • 用到大量的创建某种,某类或者某批对象时,会用到工厂模式

简单工厂简单来说就是把创建对象的方法都交给一个工厂类来实现

简单工厂模式又叫做静态工厂模式


举例:

以生产披萨为例,披萨制作分为四个步骤,写成四个方法

为了方便这里使用抽象类,一般接口用的比较多

先定义一个抽象的Pizza类,里面定义四个方法

  1. public abstract class Pizza {
  2. private String pizzaName;
  3. /**
  4. * 准备材料
  5. */
  6. void prepare() {
  7. System.out.println(pizzaName + " prepare");
  8. }
  9. /**
  10. * 烘焙
  11. */
  12. void bake() {
  13. System.out.println(pizzaName + " bake");
  14. }
  15. /**
  16. * 切
  17. */
  18. void cut() {
  19. System.out.println(pizzaName + " cut");
  20. }
  21. /**
  22. * 包装
  23. */
  24. void box() {
  25. System.out.println(pizzaName + " box");
  26. }
  27. public void setPizzaName(String pizzaName) {
  28. this.pizzaName = pizzaName;
  29. }
  30. }

然后建两个子类继承披萨父类,是为奶酪披萨和胡椒披萨

  1. public class CheesePizza extends Pizza {
  2. public CheesePizza() {
  3. this.setPizzaName("奶酪");
  4. }
  5. }
  1. public class PepperPizza extends Pizza {
  2. public PepperPizza() {
  3. this.setPizzaName("胡椒");
  4. }
  5. }

然后创建一个简单工厂类还封装创建对象的方法

根据输入的类型判断你要创建哪一种披萨

  1. public class SimpleFactory {
  2. //虽然这种方法封装了创建对象的方法
  3. //但是也违背了开闭原则
  4. //每新增一个披萨类型就需要改方法里的代码
  5. public static Pizza create(String type){
  6. if ("cheese".equals(type)){
  7. return new CheesePizza();
  8. }
  9. if ("pepper".equals(type)) {
  10. return new PepperPizza();
  11. }
  12. return null;
  13. }
  14. }

然后在获取实例的时候直接根据简单工厂类的方法就可以直接获取对象

  1. public static void main(String[] args) {
  2. Pizza pizza = SimpleFactory.create("cheese");
  3. assert pizza != null;
  4. pizza.prepare();
  5. pizza.bake();
  6. pizza.cut();
  7. pizza.box();
  8. }

简单工厂获取对象的流程就是这样
image.png
当新增披萨种类时,只需要在简单工厂类里改相应的实例化对象的代码,但是这样有违开闭原则

可能就会有疑问,这样比起直接new对象还多一个写工厂类的步骤,好像更麻烦了

那是因为这里写的代码比较少,当涉及业务扩展,功能增加,对象参数增多的时候,如果是直接new对象就需要在每个创建对象的地方改代码,而使用简单工厂模式就只需要在工厂类里改代码就行了

工厂方法模式

简单工厂模式在类的属性变多时的可维护性,可扩展性就不是很好了

而工厂方法模式是在业务扩展时不修改原有的代码,对于扩展维护比较友好

和简单工厂的区别就是

  • 简单工厂模式需要动原有的工厂类的代码
  • 工厂方法模式,不改原有工厂类代码,而是新建一个工厂类

仍以上面披萨为例

两种类型的披萨,就先创建一个工厂接口,定义好创建对象的接口,具体的实现方法交给子类完成

  1. public interface MethodFactory {
  2. Pizza createPizza();
  3. }

两个具体的创建对象的工厂类,这里写的比较简单,当有较为复杂的业务逻辑和较多的属性时可以在此封装

不同的披萨对象交由不同的工厂来生产

奶酪披萨工厂

  1. public class CheeseMethodFactory implements MethodFactory {
  2. public Pizza createPizza() {
  3. return new CheesePizza();
  4. }
  5. }
  1. public class PepperMethodFactory implements MethodFactory {
  2. public Pizza createPizza() {
  3. return new PepperPizza();
  4. }
  5. }

调用方就是这样,根据不同的工厂拿到不同的对象

  1. public static void main(String[] args) {
  2. Pizza pizza = new CheeseMethodFactory().createPizza();
  3. pizza.prepare();
  4. pizza.bake();
  5. pizza.cut();
  6. pizza.box();
  7. Pizza pizza2 = new PepperMethodFactory().createPizza();
  8. pizza2.prepare();
  9. pizza2.bake();
  10. pizza2.cut();
  11. pizza2.box();
  12. }

工厂方法模式获取实例流程就像这样,对于扩展更友好
image.png
扩展多少个种类的披萨就新建多少个生产披萨对象的工厂

抽象工厂模式

抽象工厂可以理解为生产工厂的工厂

假设小米生产手机和路由器,华为也生产手机和路由器

小米手机与华为手机同属于一个产品等级,小米手机和小米路由器同属一个产品族
image.png

以此来举例

手机产品可以指定一个接口,路由器的产品也可以指定一个接口

然后可以指定一个抽象产品工厂,围绕这个超级工厂创建其他工厂


手机产品特征接口

  1. public interface PhoneProduct {
  2. void start();
  3. void shutdown();
  4. void call();
  5. void sendSms();
  6. }

路由器产品特征

  1. public interface RouterProduct {
  2. void start();
  3. void shutdown();
  4. void openWifi();
  5. void settings();
  6. }

然后创建小米和华为的手机和路由器类实现具体的方法

就只拿华为举例复制多了没意义

华为手机类实现手机产品接口

  1. public class HuaweiPhone implements PhoneProduct {
  2. public void start() {
  3. System.out.println("华为手机开机");
  4. }
  5. public void shutdown() {
  6. System.out.println("华为手机关机");
  7. }
  8. public void call() {
  9. System.out.println("华为手机打电话");
  10. }
  11. public void sendSms() {
  12. System.out.println("华为手机发短信");
  13. }
  14. }

华为路由器类实现路由器产品接口

  1. public class HuaweiRouter implements RouterProduct {
  2. public void start() {
  3. System.out.println("华为路由器开机");
  4. }
  5. public void shutdown() {
  6. System.out.println("华为路由器关机");
  7. }
  8. public void openWifi() {
  9. System.out.println("华为路由器wifi");
  10. }
  11. public void settings() {
  12. System.out.println("华为路由器设置");
  13. }
  14. }

然后指定一个抽象产品工厂,其他工厂将围绕此工厂创建

这个工厂来指明生产哪些产品

  1. public interface ProductFactory {
  2. PhoneProduct phoneProduct();
  3. RouterProduct routerProduct();
  4. }

然后就创建一个华为的工厂,实现抽象产品工厂接口,此工厂用于创建具体的产品对象

  1. public class HuaweiFactory implements ProductFactory {
  2. public PhoneProduct phoneProduct() {
  3. return new HuaweiPhone();
  4. }
  5. public RouterProduct routerProduct() {
  6. return new HuaweiRouter();
  7. }
  8. }

最终获取对象就根据对应的工厂类来获取

  1. public static void main(String[] args) {
  2. //使用小米工厂生产小米的产品
  3. XiaomiFactory xiaomiFactory = new XiaomiFactory();
  4. PhoneProduct xiaomiPhone = xiaomiFactory.phoneProduct();
  5. RouterProduct xiaomiRouter = xiaomiFactory.routerProduct();
  6. //使用华为工厂生产华为的产品
  7. HuaweiFactory huaweiFactory = new HuaweiFactory();
  8. PhoneProduct huaweiPhone = huaweiFactory.phoneProduct();
  9. RouterProduct huaweiRouter = huaweiFactory.routerProduct();
  10. }

抽象工厂模式获取对象的方法流程大概就是这样
image.png

从设计层面看,抽象工厂模式就是对简单工厂模式的改进 或者称为进一步的抽象 。

将工厂抽象成两层

AbsFactory( 抽象工厂 ) 和 具体实现的工厂子类。

程序员可以根据创建对象类型使用对应的工厂子类。 这样将单个的简单工厂类变成了工厂族更利于代码的维护和扩展。

适用场景:

  • 不依赖产品实例如何被创建、实现等细节
  • 强调同一个产品族的一系列产品

优点:

  • 具体产品在应用层的代码隔离,无需关心细节
  • 将一个系列的产品统一到一起创建

缺点:

  • 规定了所有可能被创建的产品集合,产品族扩展新产品就会比较麻烦
  • 增加系统抽象性和理解难度