工厂模式的分类
简单工厂
在下面这段代码中,我们根据配置文件的后缀(json、xml、yaml、properties),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将存储在文件中的配置解析成内存对象 RuleConfig。
interface IRuleConfigParser {
parse(configText: string): RuleConfig;
}
class JsonRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现解析逻辑
return new RuleConfig();
}
}
class XmlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现解析逻辑
return new RuleConfig();
}
}
class YamlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现解析逻辑
return new RuleConfig();
}
}
class PropertiesRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现解析逻辑
return new RuleConfig();
}
}
class RuleConfig {}
class InvalidRuleConfigException extends Error {
constructor(message: string) {
super(message);
this.name = "InvalidRuleConfigException";
}
}
class RuleConfigSource {
load(ruleConfigFilePath: string): RuleConfig {
const ruleConfigFileExtension = this.getFileExtension(ruleConfigFilePath);
let parser: IRuleConfigParser | null = null;
switch (ruleConfigFileExtension.toLowerCase()) {
case "json":
parser = new JsonRuleConfigParser();
break;
case "xml":
parser = new XmlRuleConfigParser();
break;
case "yaml":
parser = new YamlRuleConfigParser();
break;
case "properties":
parser = new PropertiesRuleConfigParser();
break;
default:
throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath);
}
const configText = ""; // 假设从ruleConfigFilePath文件中读取配置文本到configText中
return parser.parse(configText);
}
private getFileExtension(filePath: string): string {
// 解析文件名获取扩展名,比如rule.json,返回json
return filePath.slice(((filePath.lastIndexOf(".") - 1) >>> 0) + 2);
}
}
在这个函数中,有创建 parser 和 解析两个部分,为了当代码逻辑更加清晰,可读性更好,我们要善于将功能独立的代码块封装成函数。在这里,我们把代码中涉及到 parser 创建的部分逻辑抽离出来,抽象成 createParser() 函数。重构后的代码如下所示:
interface IRuleConfigParser {
parse(configText: string): RuleConfig;
}
class JsonRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现JSON解析逻辑
return new RuleConfig();
}
}
class XmlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现XML解析逻辑
return new RuleConfig();
}
}
class YamlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现YAML解析逻辑
return new RuleConfig();
}
}
class PropertiesRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 实现Properties解析逻辑
return new RuleConfig();
}
}
class RuleConfig {}
class InvalidRuleConfigException extends Error {
constructor(message: string) {
super(message);
this.name = "InvalidRuleConfigException";
}
}
class RuleConfigSource {
load(ruleConfigFilePath: string): RuleConfig {
const ruleConfigFileExtension = this.getFileExtension(ruleConfigFilePath);
const parser = this.createParser(ruleConfigFileExtension);
const configText = ""; // 假设从ruleConfigFilePath文件中读取配置文本到configText中
return parser.parse(configText);
}
private getFileExtension(filePath: string): string {
// 解析文件名获取扩展名,比如rule.json,返回json
return filePath.slice(((filePath.lastIndexOf(".") - 1) >>> 0) + 2);
}
private createParser(fileExtension: string): IRuleConfigParser {
switch (fileExtension.toLowerCase()) {
case "json":
return new JsonRuleConfigParser();
case "xml":
return new XmlRuleConfigParser();
case "yaml":
return new YamlRuleConfigParser();
case "properties":
return new PropertiesRuleConfigParser();
default:
throw new InvalidRuleConfigException("Rule config file format is not supported: " + fileExtension);
}
}
}
为了让类更符合设计原则:单一职责(SRP),我们可以把 createParser()函数剥离到一个独立的类中,让这个类只负责对象的创建。具体的代码如下所示:
// IRuleConfigParser 接口和各种解析器实现保持不变
interface IRuleConfigParser {
parse(configText: string): RuleConfig;
}
class JsonRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
return new RuleConfig();
}
}
class XmlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
return new RuleConfig();
}
}
class YamlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
return new RuleConfig();
}
}
class PropertiesRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
return new RuleConfig();
}
}
class RuleConfig {}
class InvalidRuleConfigException extends Error {
constructor(message: string) {
super(message);
this.name = "InvalidRuleConfigException";
}
}
// RuleConfigParserFactory 类
class RuleConfigParserFactory {
static createParser(fileExtension: string): IRuleConfigParser {
switch (fileExtension.toLowerCase()) {
case "json":
return new JsonRuleConfigParser();
case "xml":
return new XmlRuleConfigParser();
case "yaml":
return new YamlRuleConfigParser();
case "properties":
return new PropertiesRuleConfigParser();
default:
throw new InvalidRuleConfigException("Rule config file format is not supported: " + fileExtension);
}
}
}
// RuleConfigSource 类使用 RuleConfigParserFactory 来创建解析器
class RuleConfigSource {
load(ruleConfigFilePath: string): RuleConfig {
const ruleConfigFileExtension = this.getFileExtension(ruleConfigFilePath);
const parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
const configText = ""; // 假设从 ruleConfigFilePath 文件中读取配置文本到 configText 中
return parser.parse(configText);
}
private getFileExtension(filePath: string): string {
return filePath.slice(((filePath.lastIndexOf(".") - 1) >>> 0) + 2);
}
}
如果 parser 可以复用,可以借助单例模式的特点,减少不必要的对象创建和内存消耗,代码如下:
class RuleConfigParserFactory {
private static instance: RuleConfigParserFactory;
// 私有化构造函数,防止外部使用new操作符创建实例
private constructor() {}
// 提供一个全局访问点用以获取实例,并确保只有一个实例被创建
public static getInstance(): RuleConfigParserFactory {
if (!RuleConfigParserFactory.instance) {
RuleConfigParserFactory.instance = new RuleConfigParserFactory();
}
return RuleConfigParserFactory.instance;
}
public createParser(fileExtension: string): IRuleConfigParser {
switch (fileExtension.toLowerCase()) {
case "json":
return new JsonRuleConfigParser();
case "xml":
return new XmlRuleConfigParser();
case "yaml":
return new YamlRuleConfigParser();
case "properties":
return new PropertiesRuleConfigParser();
default:
throw new InvalidRuleConfigException("Rule config file format is not supported: " + fileExtension);
}
}
}
class RuleConfigSource {
load(ruleConfigFilePath: string): RuleConfig {
const ruleConfigFileExtension = this.getFileExtension(ruleConfigFilePath);
// 使用单例获取 RuleConfigParserFactory 实例
const factory = RuleConfigParserFactory.getInstance();
const parser = factory.createParser(ruleConfigFileExtension);
const configText = ""; // 假设从 ruleConfigFilePath 文件中读取配置文本到 configText 中
return parser.parse(configText);
}
private getFileExtension(filePath: string): string {
return filePath.slice(((filePath.lastIndexOf(".") - 1) >>> 0) + 2);
}
}
这里的业务场景下,我们要添加 parser,肯定会改到 RuleConfigParserFactory
的代码,在简单工厂模式的代码中,有多处 if 分支判断逻辑,违背设计原则:开闭原则(OCP),但权衡拓展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加 parser,也没有太多的 parser)是没有问题的。
工厂方法
如果我们非要把 switch 分支逻辑去掉,那该怎么办呢?很简单,利用多态(Polymorphism)的实现思路,对上面的代码进行重构。
interface IRuleConfigParser {
parse(configText: string): RuleConfig;
}
class JsonRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 解析JSON配置
return new RuleConfig();
}
}
class XmlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 解析XML配置
return new RuleConfig();
}
}
class YamlRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 解析YAML配置
return new RuleConfig();
}
}
class PropertiesRuleConfigParser implements IRuleConfigParser {
parse(configText: string): RuleConfig {
// 解析Properties配置
return new RuleConfig();
}
}
class RuleConfig {
// 规则配置的具体内容
}
class InvalidRuleConfigException extends Error {
constructor(message: string) {
super(message);
this.name = "InvalidRuleConfigException";
}
}
class RuleConfigParserFactory {
private static parsers = new Map<string, { new(): IRuleConfigParser }>();
private constructor() {}
public static registerParser(extension: string, parserType: { new(): IRuleConfigParser }) {
RuleConfigParserFactory.parsers.set(extension.toLowerCase(), parserType);
}
public static createParser(fileExtension: string): IRuleConfigParser | null {
const parserConstructor = RuleConfigParserFactory.parsers.get(fileExtension.toLowerCase());
if (!parserConstructor) {
throw new InvalidRuleConfigException("Rule config file format is not supported: " + fileExtension);
}
return new parserConstructor();
}
}
// 在应用启动时注册所有解析器
function registerParsers() {
RuleConfigParserFactory.registerParser("json", JsonRuleConfigParser);
RuleConfigParserFactory.registerParser("xml", XmlRuleConfigParser);
RuleConfigParserFactory.registerParser("yaml", YamlRuleConfigParser);
RuleConfigParserFactory.registerParser("properties", PropertiesRuleConfigParser);
}
registerParsers();
class RuleConfigSource {
load(ruleConfigFilePath: string): RuleConfig {
const ruleConfigFileExtension = this.getFileExtension(ruleConfigFilePath);
const parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
if (!parser) {
throw new Error(`Parser for extension ${ruleConfigFileExtension} not found.`);
}
const configText = this.readConfigFile(ruleConfigFilePath); // 假设实现了这个方法
return parser.parse(configText);
}
private getFileExtension(filePath: string): string {
// 实现根据文件路径获取扩展名的逻辑
return filePath.slice(((filePath.lastIndexOf(".") - 1) >>> 0) + 2);
}
private readConfigFile(filePath: string): string {
// 实现读取文件内容的逻辑,这里仅为示意,实际应根据环境具体实现
return "";
}
}
当我们需要添加新的规则配置解析器的时候,我们只需要创建新的 parser 并使用 factory 注册。代码改动非常少,基本上符合设计原则:开闭原则(OCP)
借助工厂模式实现 简易 DI 容器
DI 容器相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪些其他类对象)事先创建好对象。当应用程序需要使用某个类对象的时候,直接从容器中获取即可。正是因为它持有一堆对象,所以这个框架才被称为“容器”。