原因分析
- 职责单一性违背:Factory类承担了过多不同类型对象的创建职责,没有遵循单一职责原则。原本可能只负责创建一类对象,随着项目发展,不断加入新的对象创建逻辑,导致代码臃肿。
- 缺乏抽象和分层:没有对创建逻辑进行合理抽象和分层。不同复杂程度、不同类型对象的创建逻辑都混杂在一个Factory类中,没有形成清晰的结构。
- 需求变化快速:项目需求频繁变更,每次变更都向Factory类中添加新的创建逻辑,却未对原有逻辑进行重构和优化。
识别方法
- 代码行数和复杂度:Factory类代码行数过多,方法和条件语句复杂。例如,有大量的if - else或者switch - case语句来决定创建不同类型的对象,这表明创建逻辑耦合度高,难以维护。
- 依赖关系混乱:Factory类依赖大量其他类,其内部逻辑与项目中多个模块紧密关联,牵一发而动全身,增加新的对象创建逻辑时会影响到其他部分的功能。
- 维护成本:每次修改或添加新的对象创建逻辑,都需要花费大量时间理解和调试整个Factory类的代码,而且容易引入新的错误。
改进策略
- 基于类型的工厂子类拆分
- 将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();
}
}
- 使用策略模式重构
- 定义创建对象的策略接口,每个具体的创建逻辑实现该接口。例如,创建一个
ObjectCreationStrategy
接口,有create()
方法。然后针对不同对象的创建逻辑实现具体的策略类,如UserCreationStrategy
、OrderCreationStrategy
等。
- 在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();
}
}
- 使用配置文件和反射机制
- 将对象的创建信息(如类名、构造参数等)存储在配置文件(如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;
}
}
}