MST

星途 面试题库

面试题:Java架构中Factory模式的反模式识别与改进

假设在一个大型Java项目中使用了Factory模式,随着项目的演进,出现了Factory类变得过于庞大,创建对象的逻辑复杂且难以维护的情况,这很可能是一种反模式。请分析这种Factory反模式产生的原因,如何识别它,并且提出至少两种改进策略。
19.0万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

原因分析

  1. 职责单一性违背:Factory类承担了过多不同类型对象的创建职责,没有遵循单一职责原则。原本可能只负责创建一类对象,随着项目发展,不断加入新的对象创建逻辑,导致代码臃肿。
  2. 缺乏抽象和分层:没有对创建逻辑进行合理抽象和分层。不同复杂程度、不同类型对象的创建逻辑都混杂在一个Factory类中,没有形成清晰的结构。
  3. 需求变化快速:项目需求频繁变更,每次变更都向Factory类中添加新的创建逻辑,却未对原有逻辑进行重构和优化。

识别方法

  1. 代码行数和复杂度:Factory类代码行数过多,方法和条件语句复杂。例如,有大量的if - else或者switch - case语句来决定创建不同类型的对象,这表明创建逻辑耦合度高,难以维护。
  2. 依赖关系混乱:Factory类依赖大量其他类,其内部逻辑与项目中多个模块紧密关联,牵一发而动全身,增加新的对象创建逻辑时会影响到其他部分的功能。
  3. 维护成本:每次修改或添加新的对象创建逻辑,都需要花费大量时间理解和调试整个Factory类的代码,而且容易引入新的错误。

改进策略

  1. 基于类型的工厂子类拆分
    • 将Factory类按照创建对象的类型拆分成多个子类。例如,如果原Factory类负责创建用户、订单和商品对象,可分别创建UserFactory、OrderFactory和ProductFactory类。每个子类只负责创建特定类型的对象,这样每个子类的职责明确,代码量和复杂度降低。
    • 在代码实现上,原Factory类可作为抽象类,定义创建对象的抽象方法,子类继承并实现这些方法。如:
abstract class AbstractFactory {
    abstract Object createObject();
}

class UserFactory extends AbstractFactory {
    @Override
    Object createObject() {
        // 创建用户对象的逻辑
        return new User();
    }
}

class OrderFactory extends AbstractFactory {
    @Override
    Object createObject() {
        // 创建订单对象的逻辑
        return new Order();
    }
}
  1. 使用策略模式重构
    • 定义创建对象的策略接口,每个具体的创建逻辑实现该接口。例如,创建一个ObjectCreationStrategy接口,有create()方法。然后针对不同对象的创建逻辑实现具体的策略类,如UserCreationStrategyOrderCreationStrategy等。
    • 在Factory类中使用这些策略,通过依赖注入的方式将具体策略传递给Factory类。这样,Factory类只负责管理策略,而不包含具体的创建逻辑。代码示例如下:
interface ObjectCreationStrategy {
    Object create();
}

class UserCreationStrategy implements ObjectCreationStrategy {
    @Override
    public Object create() {
        // 创建用户对象的逻辑
        return new User();
    }
}

class OrderCreationStrategy implements ObjectCreationStrategy {
    @Override
    public Object create() {
        // 创建订单对象的逻辑
        return new Order();
    }
}

class StrategyBasedFactory {
    private ObjectCreationStrategy strategy;

    public StrategyBasedFactory(ObjectCreationStrategy strategy) {
        this.strategy = strategy;
    }

    public Object createObject() {
        return strategy.create();
    }
}
  1. 使用配置文件和反射机制
    • 将对象的创建信息(如类名、构造参数等)存储在配置文件(如XML或properties文件)中。当需要创建对象时,Factory类读取配置文件,通过反射机制根据类名创建对象,并设置相应的属性。
    • 例如,在XML配置文件中定义:
<object>
    <class>com.example.User</class>
    <property name="name" value="John"/>
</object>
- 在Factory类中,使用DOM或SAX解析XML文件,获取类名和属性信息,通过反射创建对象:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
public class ConfigBasedFactory {
    public Object createObjectFromConfig(String configFilePath) {
        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(new File(configFilePath));
            doc.getDocumentElement().normalize();
            NodeList classNodes = doc.getElementsByTagName("class");
            String className = classNodes.item(0).getTextContent();
            Class<?> clazz = Class.forName(className);
            Constructor<?> constructor = clazz.getConstructor();
            Object obj = constructor.newInstance();
            NodeList propertyNodes = doc.getElementsByTagName("property");
            for (int i = 0; i < propertyNodes.getLength(); i++) {
                Element propertyElement = (Element) propertyNodes.item(i);
                String propertyName = propertyElement.getAttribute("name");
                String propertyValue = propertyElement.getAttribute("value");
                Field field = clazz.getDeclaredField(propertyName);
                field.setAccessible(true);
                field.set(obj, propertyValue);
            }
            return obj;
        } catch (ParserConfigurationException | IOException | SAXException | ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException e) {
            e.printStackTrace();
            return null;
        }
    }
}