目录
1 背景
1.1 题目描述
小明家新开了两个工厂用来生产家具,一个生产现代风格的沙发和椅子,一个生产古典风格的沙发和椅子,现在工厂收到了一笔订单,请你帮他设计一个系统,描述订单需要生产家具的信息。
1.2 输入描述
输入的第一行是一个整数 N(1 ≤ N ≤ 100),表示订单的数量。
接下来的 N 行,每行输入一个字符串,字符串表示家具的类型。家具类型分为 “modern” 和 “classical” 两种。
1.3 输出描述
对于每笔订单,输出字符串表示该订单需要生产家具的信息。
modern订单会输出下面两行字符串
modern chair
modern sofa
classical订单会输出下面两行字符串
classical chair
classical soft
1.4 输入示例
3
modern
classical
modern
1.5 输出示例
modern chair
modern sofa
classical chair
classical sofa
modern chair
modern sofa
2 抽象工厂模式
- 代码示例:【来源】
import java.util.Scanner;
// 抽象椅子接口
interface Chair {
void showInfo();
}
// 具体现代风格椅子
class ModernChair implements Chair {
@Override
public void showInfo() {
System.out.println("modern chair");
}
}
// 具体古典风格椅子
class ClassicalChair implements Chair {
@Override
public void showInfo() {
System.out.println("classical chair");
}
}
// 抽象沙发接口
interface Sofa {
void displayInfo();
}
// 具体现代风格沙发
class ModernSofa implements Sofa {
@Override
public void displayInfo() {
System.out.println("modern sofa");
}
}
// 具体古典风格沙发
class ClassicalSofa implements Sofa {
@Override
public void displayInfo() {
System.out.println("classical sofa");
}
}
// 抽象家居工厂接口
interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
}
// 具体现代风格家居工厂
class ModernFurnitureFactory implements FurnitureFactory {
@Override
public Chair createChair() {
return new ModernChair();
}
@Override
public Sofa createSofa() {
return new ModernSofa();
}
}
// 具体古典风格家居工厂
class ClassicalFurnitureFactory implements FurnitureFactory {
@Override
public Chair createChair() {
return new ClassicalChair();
}
@Override
public Sofa createSofa() {
return new ClassicalSofa();
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取订单数量
int N = scanner.nextInt();
// 处理每个订单
for (int i = 0; i < N; i++) {
// 读取家具类型
String furnitureType = scanner.next();
// 创建相应风格的家居装饰品工厂
FurnitureFactory factory = null;
if (furnitureType.equals("modern")) {
factory = new ModernFurnitureFactory();
} else if (furnitureType.equals("classical")) {
factory = new ClassicalFurnitureFactory();
}
// 根据工厂生产椅子和沙发
Chair chair = factory.createChair();
Sofa sofa = factory.createSofa();
// 输出家具信息
chair.showInfo();
sofa.displayInfo();
}
}
}
3 思考
- 这真的是抽象工厂模式吗?太不灵活了。
- 在上面的背景下,新增风格,新增家具,是很常见的需求。而按照上面的写法,需要改动很多代码才能实现。这不就完全没体现抽象工厂模式的优势了?
3.1 我的实现
public class Main {
public static void main(String[] args) {
FurnitureFactorySystem furnitureFactorySystem = FurnitureFactorySystem.getSingleton();
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine());
for (int i = 0; i < n; i ++) {
String type = scanner.nextLine();
furnitureFactorySystem.produce(type);
}
scanner.close();
}
}
/**
* 生产限制:根据风格,成套出售。(对应现实生活,手机配色,对应整部手机)
*/
class FurnitureFactorySystem {
private static final Map<StyleEnum, FurnitureFactory> furnitureFactoryMap = new HashMap<>();
private static FurnitureFactorySystem instance;
private FurnitureFactorySystem() {
furnitureFactoryMap.put(StyleEnum.MODERN, new ModernFurnitureFactory());
furnitureFactoryMap.put(StyleEnum.CLASSIC, new ClassicFurnitureFactory());
}
public static FurnitureFactorySystem getSingleton() {
if (instance == null) {
synchronized (FurnitureFactorySystem.class) {
if (instance == null) {
instance = new FurnitureFactorySystem();
}
}
}
return instance;
}
public void produce(String type) {
FurnitureFactory furnitureFactory = furnitureFactoryMap.get(StyleEnum.of(type));
final List<Furniture> furnitures = Arrays.asList(new Chair(), new Sofa());
furnitureFactory.createFurniture(furnitures);
}
}
interface FurnitureFactory {
void createFurniture(List<Furniture> furnitures);
}
class ModernFurnitureFactory implements FurnitureFactory {
@Override
public void createFurniture(List<Furniture> furnitures) {
for (Furniture furniture : furnitures) {
furniture.create(StyleEnum.MODERN.getValue());
}
}
}
class ClassicFurnitureFactory implements FurnitureFactory {
@Override
public void createFurniture(List<Furniture> furnitures) {
for (Furniture furniture : furnitures) {
furniture.create(StyleEnum.CLASSIC.getValue());
}
}
}
interface Furniture {
void create(String type);
}
class Chair implements Furniture {
@Override
public void create(String type) {
System.out.println(type + " " + FurnitureEnum.CHAIR.getValue());
}
}
class Sofa implements Furniture {
@Override
public void create(String type) {
System.out.println(type + " " + FurnitureEnum.SOFA.getValue());
}
}
@AllArgsConstructor
@Getter
enum StyleEnum {
MODERN("modern"), CLASSIC("classical");
private final String value;
public static StyleEnum of(String value) {
for (StyleEnum styleEnum : StyleEnum.values()) {
if (styleEnum.getValue().equals(value)) {
return styleEnum;
}
}
// 如果没有找到匹配的枚举对象,可以抛出一个异常或返回null
throw new IllegalArgumentException("Unknown StyleEnum: " + value);
}
}
/**
* 家具
*/
@AllArgsConstructor
@Getter
enum FurnitureEnum {
CHAIR("chair"), SOFA("sofa");
private final String value;
public static FurnitureEnum of(String value) {
for (FurnitureEnum furnitureEnum : FurnitureEnum.values()) {
if (furnitureEnum.getValue().equals(value)) {
return furnitureEnum;
}
}
// 如果没有找到匹配的枚举对象,可以抛出一个异常或返回null
throw new IllegalArgumentException("Unknown FurnitureEnum: " + value);
}
}
- 假设要新增风格和新家具,需要改动的代码:
// 新增语句
private FurnitureFactorySystem() {
furnitureFactoryMap.put(StyleEnum.MODERN, new ModernFurnitureFactory());
furnitureFactoryMap.put(StyleEnum.CLASSIC, new ClassicFurnitureFactory());
furnitureFactoryMap.put(StyleEnum.xxx, new xxxFurnitureFactory());
}
public void produce(String type) {
FurnitureFactory furnitureFactory = furnitureFactoryMap.get(StyleEnum.of(type));
final List<Furniture> furnitures = Arrays.asList(new Chair(), new Sofa(), yyy); // 新增家具
furnitureFactory.createFurniture(furnitures);
}
// 新增类
class xxxFurnitureFactory implements FurnitureFactory {
@Override
public void createFurniture(List<Furniture> furnitures) {
for (Furniture furniture : furnitures) {
furniture.create(StyleEnum.xxx.getValue());
}
}
}
// 新增类
class yyy implements Furniture {
@Override
public void create(String type) {
System.out.println(type + " " + FurnitureEnum.yyy.getValue());
}
}
// 新增枚举
enum StyleEnum {
MODERN("modern"), CLASSIC("classical"), xxx;
}
// 新增枚举
enum FurnitureEnum {
CHAIR("chair"), SOFA("sofa"), yyy;
}
3.2 什么时候用抽象工厂模式?(怎么用才是合适的?)
- 抽象工厂在实际的项目中相对也不常用,了解即可。
结论源自《设计模式之美》。
- “3.1 我的实现”看似更灵活,但实际上不符合实际需要。
class Chair implements Furniture {
@Override
public void create(String type) {
System.out.println(type + " " + FurnitureEnum.CHAIR.getValue());
}
}
class Sofa implements Furniture {
@Override
public void create(String type) {
System.out.println(type + " " + FurnitureEnum.SOFA.getValue());
}
}
- 实际上,就是需要ModernChair、ModernSofa、ClassicalChair、ClassicalSofa这种对象。
3.3 更好的例子
3.3.1 背景
-
按钮和复选框,macos和windows,现实就存在4种对象:
- macos按钮、macos复选框
- windows按钮、windows复选框
-
很显然,代码中就需要4个实体类与之对应。
-
对于应用层,需要操作按钮和复选框,前提是获取对应的对象。而不同操作系统,返回的对象应该不同。
- 此时,工厂不止生产一个对象,要同时能生产按钮对象和复选框对象。由于工厂方法模式的特点是一个工厂就生产一个对象。因此,引入抽象工厂模式。
3.3.2 示例
public class Example {
public static void main(String[] args) {
GuiFactorySystem.paint("macos");
GuiFactorySystem.paint("windows");
}
}
interface Button {
void paint();
}
class MacosButton implements Button {
public void paint()
{
System.out.println("MacOS Button");
}
}
class WindowsButton implements Button {
public void paint()
{
System.out.println("Windows Button");
}
}
interface Checkbox {
void paint();
}
class MacosCheckbox implements Checkbox {
public void paint()
{
System.out.println("MacOS Checkbox");
}
}
class WindowsCheckbox implements Checkbox {
public void paint()
{
System.out.println("Windows Checkbox");
}
}
// 抽象工厂
interface GuiFactory {
Button createButton();
Checkbox createCheckbox();
}
class MacosGuiFactory implements GuiFactory {
public Button createButton()
{
return new MacosButton();
}
public Checkbox createCheckbox()
{
return new MacosCheckbox();
}
}
class WindowsGuiFactory implements GuiFactory {
public Button createButton()
{
return new WindowsButton();
}
public Checkbox createCheckbox()
{
return new WindowsCheckbox();
}
}
class GuiFactorySystem {
private static final Map<String, GuiFactory> guiFactoryMap = new HashMap<>();
static {
guiFactoryMap.put("macos", new MacosGuiFactory());
guiFactoryMap.put("windows", new WindowsGuiFactory());
}
private GuiFactorySystem() {
}
public static void paint(String type) {
GuiFactory guiFactory = guiFactoryMap.get(type);
Button button = guiFactory.createButton();
Checkbox checkbox = guiFactory.createCheckbox();
button.paint();
checkbox.paint();
}
// 也可以单独返回button对象,或者checkbox对象
}
- 与工厂方法模式,最大的差别:
// 抽象工厂
interface GuiFactory {
Button createButton();
Checkbox createCheckbox();
}
- 也不是不能用工厂方法模式实现,那就要4个工厂类(MacosButtonFactory, WindowsButtonFactory, MacosCheckboxFactory, WindowsCheckboxFactory)。而抽象工厂模式只用了2个工厂类(WindowsGuiFactory、MacosGuiFactory)。
- 实际开发中,简单工厂模式、工厂方法模式用的更多。