记录一下对工厂模式的学习
参考奔波霸取经-工厂模式的设计思想; 奔波霸取经-六大设计原则; 奔波霸取经-范型使用到原理
为何要使用工厂模式?
作为“产品”的使用者,不应该关注产品的创建过程。比如我现在要创建一个电脑,我只希望一行代码能返回一个电脑,而不是对着computer对象一顿猛set,这显然是不合理的。
public static void main(String[] args) {
//把创建、组装电脑的过程暴露给使用者,这不合理
Computer computer = new Computer();
computer.setCoreType("i7");
computer.setScreenSize("13'");
computer.setStorageSize("16G");
computer.setType("Notebook");
}
工厂模式-简单工厂
意图: 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
何时使用: 我们明确地计划不同条件下创建不同实例时。
代码:
// 顶层的抽象产品
public abstract class Screen {
public abstract void display();
}
// Screen的具体实现类
class LedScreen extends Screen {
@Override
public void display() {
System.out.println("使用Led屏幕玩PS5");
}
}
// Screen的具体实现类
class OledScreen extends Screen {
@Override
public void display() {
System.out.println("使用Oled屏幕看超清大片");
}
}
// 工厂类,仅有一个静态方法,根据传入的type去创建对应的"产品"
public class ScreenFactory {
public static Screen createScreen(ScreenType screenType) {
switch (screenType) {
case LED:
return new LedScreen();
case OLED:
return new OledScreen();
}
return null;
}
public enum ScreenType {
LED,
OLED
}
}
// 工厂类,由之前直接创建产品,改为创建用来生产具体产品的创建者
public class ScreenFactoryCreator {
private static final Map<String, ScreenCreator> cache = new HashMap<>();
//静态加载,提前存放,如果将来有其他的工厂,也可以在这里维护
static {
cache.put(LedScreenCreator.class.getName(), new LedScreenCreator());
cache.put(OledScreenCreator.class.getName(), new OledScreenCreator());
}
//创建工厂
public static <T extends ScreenCreator> T createFactory(Class<T> tClass) {
String name = tClass.getName();
if (cache.containsKey(name)) {
return (T) cache.get(name);
}
try {
ScreenCreator screenCreator = (ScreenCreator) Class.forName(name).newInstance();
cache.put(name, screenCreator);
return (T) screenCreator;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
使用:
public static void main(String[] args) {
Screen ledScreen = ScreenFactory.createScreen(ScreenFactory.ScreenType.LED);
ledScreen.display();
Screen oledScreen = ScreenFactory.createScreen(ScreenFactory.ScreenType.OLED);
oledScreen.display();
}
优点:
使用方便,使用者一行代码就能获得Screen对象;屏蔽产品的具体实现,调用者只关心产品的创建接口。
缺点:
返回的是抽象对象而非具体产品对象,有点让人摸不到头脑。
使用者创建产品时,需要在createScreen中进行产品类型的匹配,如果有100个产品那么最多可能需要判断100次才会最终创建,执行效率非常低;每次新增一个“产品”,都需要到工厂类createScreen方法之中,增加对应的if else或者case,产品越多,可读性越差,维护成本越高。
既然一个工厂生产多个产品不易维护,那么我们升级一下,让“一个工厂只生产一个产品”,让调用者把注意力由产品转移到对应的工厂即可。
工厂模式-多工厂
一个工厂对应一个产品
// 顶层的抽象产品
public abstract class Screen {
public abstract void display();
}
// 抽象的产品创建者,返回一个抽象的产品
public abstract class ScreenCreator<T extends Screen> {
public abstract T createScreen();
}
// 产品创建者的实现类,Led屏幕创建者,生产Led屏幕
public class LedScreenCreator extends ScreenCreator<LedScreen> {
@Override
public LedScreen createScreen() {
return new LedScreen();
}
}
// 产品创建者的实现类,Oled屏幕创建者,生产Oled屏幕
public class OledScreenCreator extends ScreenCreator<OledScreen> {
@Override
public OledScreen createScreen() {
return new OledScreen();
}
}
//使用
LedScreen ledScreen = ScreenFactoryCreator.createFactory(LedScreenCreator.class).createScreen();
OledScreen oledScreen = ScreenFactoryCreator.createFactory(OledScreenCreator.class).createScreen();
ledScreen.watchAV();
oledScreen.playPS5();
}
优点:
这里我使用了范型,确保返回的产品为具体实现类而非抽象,避免了调用者对顶层抽象进行强转的尴尬。关于范型的介绍与使用,可以参考范型使用到原理
让一个工厂生产一个产品,调用者只关注具体的工厂即可,一行代码创建出对应的产品,使用起来非常方便。
在ScreenFactoryCreator中,使用map对具体工厂进行预加载和管理,每次新增产品时只需创建对应的工厂以及产品,并维护到map中,肥肠的方便。如若产品很多,为避免不必要的资源浪费,不提前存放工厂也是可以的。