设计模式-工厂模式

160 阅读3分钟

记录一下对工厂模式的学习

参考奔波霸取经-工厂模式的设计思想奔波霸取经-六大设计原则奔波霸取经-范型使用到原理

为何要使用工厂模式?

作为“产品”的使用者,不应该关注产品的创建过程。比如我现在要创建一个电脑,我只希望一行代码能返回一个电脑,而不是对着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中,肥肠的方便。如若产品很多,为避免不必要的资源浪费,不提前存放工厂也是可以的。