What

什么时候需要使用工厂模式?

  • 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明;
  • 代码复用:创建代码抽离到独立的工厂类之后可以复用;
  • 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象;
  • 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。

描述 场景 开闭原则
简单工厂 一个工厂创建多个对象 创建不同类的实例,且创建过程比较简单 符合
工厂方法 一个工厂创建一个对象 创建相同类的实例,且创建过程比较复杂 不符合
抽象工厂 一个工厂创建多个对象 创建不同类型的实例,且创建过程比较复杂

How

简单工厂

一个工厂类,不同类型的实例都在该工厂类中创建。适用于类型相对固定、单一,且创建过程比较简单的场景

  • 方式一:在工厂类中通过 if else 创建不同的对象
  • 方式二:在方式一的基础上 + 策略模式 消除了 if else判断,且对象单例

使用简单工厂可以帮助消费者直接创建产品的责任,实现了责任的分割,但存在的问题是,一但要增加新的产品,必须要修改工厂类,不符合开闭原则

  1. /**
  2. * 简单工厂
  3. * 将创建对象的过程封装在工厂类的内部
  4. * 场景:适用于类型相对固定、单一,且创建过程比较复杂的场景
  5. * @author abley
  6. * @date 2022/1/4 20:44
  7. */
  8. public class SimpleFactory {
  9. public static Fruit getFruit(String name) {
  10. if ("Apple".equalsIgnoreCase(name)) {
  11. return new Apple();
  12. }
  13. if ("Pear".equalsIgnoreCase(name)) {
  14. return new Pear();
  15. }
  16. return null;
  17. }
  18. }
  19. /**
  20. * 简单工厂2
  21. * - 结合单例模式
  22. */
  23. class SimpleFactory2 {
  24. private static final Map<String, Fruit> FRUIT_MAP = new HashMap<>();
  25. static {
  26. FRUIT_MAP.put("Apple", new Apple());
  27. FRUIT_MAP.put("Pear", new Pear());
  28. }
  29. public static Fruit getFruit(String name) {
  30. return FRUIT_MAP.get(name);
  31. }
  32. }
  33. /**
  34. * 水果接口
  35. */
  36. interface Fruit {
  37. /**
  38. * 名称
  39. */
  40. String name();
  41. }
  42. class Apple implements Fruit {
  43. @Override
  44. public String name() {
  45. return "我是苹果!";
  46. }
  47. }
  48. class Pear implements Fruit {
  49. @Override
  50. public String name() {
  51. return "我是梨!";
  52. }
  53. }

工厂方法🌟

多个工厂类,为每个类型的实例对象定义一个对应的工厂类。当出现新的同类型需求时,只需要实现工厂接口就可以定制自己的的创建工厂,无需修改原来的代码,符合开闭原则。适用于实例对象的创建不仅复杂而且多样化的场景。

/**
 * 工厂方法
 * <p>
 * 在简单工厂的基础上 符合开闭原则
 * 多个工厂类,为每个类型的实例对象定义一个对应的工厂类;
 * 场景:适用于实例对象的创建不仅复杂而且多样化的场景
 *
 * @author yiy
 * @date 2022/7/7 10:10
 */
public class MethodFactory {

}

/**
 * 工厂方法接口
 */
interface MethodFruitFactory {
    /**
     * 获取水果
     *
     * @return 水果
     */
    Fruit getFruit();
}

/**
 * 水果接口
 */
interface Fruit {
    /**
     * 名称
     */
    String name();
}

/**
 * 苹果工厂
 */
class AppleFactory implements MethodFruitFactory {

    @Override
    public Fruit getFruit() {
        return new Apple();
    }
}

/**
 * 梨工厂
 */
class PearFactory implements MethodFruitFactory {

    @Override
    public Fruit getFruit() {
        return new Pear();
    }
}

class Apple implements Fruit {

    @Override
    public String name() {
        return "我是苹果!";
    }
}

class Pear implements Fruit {

    @Override
    public String name() {
        return "我是梨!";
    }
}

抽象工厂

  • 场景:适用分类复杂的对象创建。即 让一个工厂负责创建多个不同类型的对象,而不是只创建一种类型的对象。 ```java /**
    • 抽象工厂
    • 下面例子通过应用抽象工厂模式, 使得客户端代码无需与具体 UI 类耦合, 就能创建跨平台的 UI 元素, 同时确保所创建的元素与指定的操作系统匹配。
    • 跨平台应用中的相同 UI 元素功能类似, 但是在不同操作系统下的外观有一定差异。 此外, 你需要确保 UI 元素与当前操作系统风格一致。 你一定不希望在 Windows 系统下运行的应用程序中显示 macOS 的控件。
    • 抽象工厂接口声明一系列构建方法, 客户端代码可调用它们生成不同风格的 UI 元素。 每个具体工厂对应特定操作系统, 并负责生成符合该操作系统风格的 UI 元素。
    • 其运作方式如下: 应用程序启动后检测当前操作系统。 根据该信息, 应用程序通过与该操作系统对应的类创建工厂对象。 其余代码使用该工厂对象创建 UI 元素。 这样可以避免生成错误类型的元素。
    • 使用这种方法, 客户端代码只需调用抽象接口, 而无需了解具体工厂类和 UI 元素。 此外, 客户端代码还支持未来添加新的工厂或 UI 元素。
    • 这样一来, 每次在应用程序中添加新的 UI 元素变体时, 你都无需修改客户端代码。 你只需创建一个能够生成这些 UI 元素的工厂类, 然后稍微修改应用程序的初始代码, 使其能够选择合适的工厂类即可。 *
    • @author abley
    • @date 2022/7/6 23:17 */ public class AbstractFactory { }

/**

  • GUI抽象工厂类
  • 注:此处也可以使用接口来定义,但为了符合抽象工厂的名称我还是用抽象类吧 */ abstract class GUIFactoryAbstract {

    /**

    • 创建按钮的接口
    • 注:此处亦可以通过传递不同的参数去创建同一类型下的更加细分的Button对象 *
    • @return */ public abstract Button createButton();

      public abstract CheckBox createCheckBox(); }

/**

  • windows工厂 */ class WinFactory extends GUIFactoryAbstract {
@Override
public Button createButton() {
    return new WinButton();
}

@Override
public CheckBox createCheckBox() {
    return new WinCheckbox();
}

}

/**

  • Mac工厂 */ class MacFactory extends GUIFactoryAbstract {
@Override
public Button createButton() {
    return new MacButton();
}

@Override
public CheckBox createCheckBox() {
    return new MacCheckbox();
}

}

interface Button { /**

 * 渲染成按钮
 */
void paint();

}

interface CheckBox { /**

 * 渲染成选择框
 */
void paint();

}

class WinButton implements Button {

@Override
public void paint() {
    System.out.println("渲染成Windows按钮");
}

}

class WinCheckbox implements CheckBox {

@Override
public void paint() {
    System.out.println("渲染成Windows选择框");
}

}

class MacButton implements Button {

@Override
public void paint() {
    System.out.println("渲染成macOS按钮");
}

}

class MacCheckbox implements CheckBox {

@Override
public void paint() {
    System.out.println("渲染成macOS选择框");
}

}

/**

  • 抽象工厂客户端(工厂的工厂) / class GUIClient { /*

    • 获取工厂类
    • 根据不同的平台创建不同的GUI工厂 *
    • @param platform 平台类型,此处可以使用枚举代替更好。同时也可以在程序启动时通过读取平台信息自动初始化。
    • @return GUI工厂 */ public static GUIFactoryAbstract getFactory(String platform) { if (“Mac”.equalsIgnoreCase(platform)) {

       return new MacFactory();
      

      } if (“Win”.equalsIgnoreCase(platform)) {

       return new WinFactory();
      

      } return null; } }

      ```java
      public class Test {
      public static void main(String[] args) {
       //根据不同的系统创建不同的控件
       Button button = GUIClient.getFactory(System.getProperty("os.name")).createButton();
       CheckBox checkBox = GUIClient.getFactory(System.getProperty("os.name")).createCheckBox();
      
       //创建macOS平台的按钮
       Button macButton = GUIClient.getFactory("Mac").createButton();
       //创建windows平台的按钮
       Button winButton = GUIClient.getFactory("Win").createButton();
       //创建macOS平台的选择框
       CheckBox macCheckBox = GUIClient.getFactory("Mac").createCheckBox();
       //创建windows平台的选择框
       CheckBox winCheckBox = GUIClient.getFactory("Win").createCheckBox();
      }
      }