What
什么时候需要使用工厂模式?
- 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明;
- 代码复用:创建代码抽离到独立的工厂类之后可以复用;
- 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象;
- 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
描述 | 场景 | 开闭原则 | |
---|---|---|---|
简单工厂 | 一个工厂创建多个对象 | 创建不同类的实例,且创建过程比较简单 | 符合 |
工厂方法 | 一个工厂创建一个对象 | 创建相同类的实例,且创建过程比较复杂 | 不符合 |
抽象工厂 | 一个工厂创建多个对象 | 创建不同类型的实例,且创建过程比较复杂 |
How
简单工厂
一个工厂类,不同类型的实例都在该工厂类中创建。适用于类型相对固定、单一,且创建过程比较简单的场景
- 方式一:在工厂类中通过 if else 创建不同的对象
- 方式二:在方式一的基础上 + 策略模式 消除了 if else判断,且对象单例
使用简单工厂可以帮助消费者直接创建产品的责任,实现了责任的分割,但存在的问题是,一但要增加新的产品,必须要修改工厂类,不符合开闭原则。
/**
* 简单工厂
* 将创建对象的过程封装在工厂类的内部
* 场景:适用于类型相对固定、单一,且创建过程比较复杂的场景
* @author abley
* @date 2022/1/4 20:44
*/
public class SimpleFactory {
public static Fruit getFruit(String name) {
if ("Apple".equalsIgnoreCase(name)) {
return new Apple();
}
if ("Pear".equalsIgnoreCase(name)) {
return new Pear();
}
return null;
}
}
/**
* 简单工厂2
* - 结合单例模式
*/
class SimpleFactory2 {
private static final Map<String, Fruit> FRUIT_MAP = new HashMap<>();
static {
FRUIT_MAP.put("Apple", new Apple());
FRUIT_MAP.put("Pear", new Pear());
}
public static Fruit getFruit(String name) {
return FRUIT_MAP.get(name);
}
}
/**
* 水果接口
*/
interface Fruit {
/**
* 名称
*/
String name();
}
class Apple implements Fruit {
@Override
public String name() {
return "我是苹果!";
}
}
class Pear implements Fruit {
@Override
public String name() {
return "我是梨!";
}
}
工厂方法🌟
多个工厂类,为每个类型的实例对象定义一个对应的工厂类。当出现新的同类型需求时,只需要实现工厂接口就可以定制自己的的创建工厂,无需修改原来的代码,符合开闭原则。适用于实例对象的创建不仅复杂而且多样化的场景。
/**
* 工厂方法
* <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(); } }