本文使用了王争老师设计模式课程中的例子,写的很清晰,而且中间穿插了代码优化。
由于设计模式就是解决问题的一种思路,所以每个设计模式会从问题出发,这样比较好理解设计模式出现的意义。
解决问题:在调用时不想判断来实例化哪一个类或者实例化的过程过于复杂。
举个例子:我们读取配置文件,根据配置文件的后缀(json,xml,yaml)来选择不同解析器(JsonRuleConfigParser
、XmlRuleConfigParser
、YamlRuleConfigParser
),最后将文件解析为RuleConfig格式。这样具体调用的时候我们就不需要管到底实例化哪一个解析器,只需要传入需要解析的文件路径。
1、定义解析器接口
public interface IRuleConfigParser {
}
2、定义各类解析器
public class JsonRuleConfigParser implements IRuleConfigParser{
}
public class XmlRuleConfigParser implements IRuleConfigParser{
}
public class YamlRuleConfigParser implements IRuleConfigParser{
}
3、定义工厂类
public class RuleConfigParserFactory {
public IRuleConfigParser getParserInstance(String path) {
//获取文件后缀
String fileExtension = getFileExtension();
IRuleConfigParser parser = null;
if ("json".equals(fileExtension)) {
parser = new JsonRuleConfigParser();
} else if ("xml".equals(fileExtension)) {
parser = new XmlRuleConfigParser();
} else if ("yaml".equals(fileExtension)) {
parser = new YamlRuleConfigParser();
}
return parser;
}
}
总结:简单工厂模式就是把创建对象的活单独抽出来放一个工厂类中。
优点:对象的创建和使用进行了分离,如果创建方式改了只修改工厂类就可以了。
缺点:扩展性差,加一个新的对象的时候需要修改工厂文件,增加if-else。
适用工厂方法优化上面例子,代码如下:
1、给每个解析器创建一个工厂类,工厂类又实现了IRuleConfigParserFactory
接口;
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser();
}
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new YamlRuleConfigParser();
}
}
2、工厂抽象出来后,看看怎么用:
public class RuleConfigParserFactory {
public IRuleConfigParser getParserInstance(String path) {
//获取文件后缀
String fileExtension = getFileExtension();
IRuleConfigParserFactory factory= null;
if ("json".equals(fileExtension)) {
factory = new JsonRuleConfigParserFactory();
} else if ("xml".equals(fileExtension)) {
factory = new XmlRuleConfigParserFactory();
} else if ("yaml".equals(fileExtension)) {
factory = new YamlRuleConfigParserFactory();
}
return factory.createParser();
}
}
看完上面的代码是不是觉得又进入了if-else
的圈子。看了王争老师的优化,优化后代码如下,通过单例可以避免if-else
,但是这种方法也有局限性,如果每次需要创建不同的新对象,下面的方法就不行了:
public class JsonRuleConfigParserFactoryMap {
private static final Map<String, IRuleConfigParserFactory> cachedFactory = new HashMap<>();
static {
cachedFactory.put("json", new JsonRuleConfigParserFactory());
cachedFactory.put("xml", new XmlRuleConfigParserFactory());
cachedFactory.put("yaml", new YamlRuleConfigParserFactory());
}
public static IRuleConfigParserFactory getRuleConfigParserFactory(String fileExtension) {
if (TextUtils.isEmpty(fileExtension)) {
return null;
}
return cachedFactory.get(fileExtension);
}
}
把每个解析器的创建又封装到了工厂类里,我个人觉得除了代码结构变复杂了,没看到工厂方法比简单工厂好在哪。我看了很多例子,大体都是这样,要么没有写最后的用法,要么僻重就轻把使用改成如下:
JsonRuleConfigParserFactory jsonParserFactory = new JsonRuleConfigParserFactory();
IRuleConfigParser jsonParser = jsonParserFactory.createParser();
所以学到这里,工厂方法的优势没有get到,最终的使用还是要if-else
判断,并且每增加一个解析器就会增加一个Factory
类。
看王争老师总结之所以会有上面的疑惑是因为:这种简单的使用场景其实并不适合使用工厂方法,因为这个例子中,工厂方法需要额外建Factory
类,并且类里就一句new
对象代码,没必要设计成独立类,所以对于这个例子,简单工厂比工厂方法更适合。
工厂方法适用场景:对象的创建比较复杂,不只是简单new
,例如还要组合其他类,做各种初始化操作,这种才比较适合工厂方法场景。
下面是刚才简单工厂获取解析器的逻辑,如果获取parser
的方式比较复杂,还需要组合其他类,做各种不同的初始化操作,这样getParserInstance
函数逻辑就会比较复杂,就需要单独拆出工厂类去创建才会更合理。
public class RuleConfigParserFactory {
public IRuleConfigParser getParserInstance(String path) {
//获取文件后缀
String fileExtension = getFileExtension();
IRuleConfigParser parser = null;
if ("json".equals(fileExtension)) {
parser = new JsonRuleConfigParser();
} else if ("xml".equals(fileExtension)) {
parser = new XmlRuleConfigParser();
} else if ("yaml".equals(fileExtension)) {
parser = new YamlRuleConfigParser();
}
return parser;
}
}
抽象工厂主要在工厂方法的基础上解决多分类问题,前面我们获取解析器是通过RuleConfig规则来区分的,如果再加一个分类,要通过系统配置规则来区分的呢?使用工厂方法模式,我们需要添加三个解析器,再加三个工厂类。扩展的时候太过繁琐,为解决这个问题,可以再抽象出一个接口。
JsonRuleConfigParser()
XmlRuleConfigParser()
YamlRuleConfigParser()
//需要增加的
JsonSystemConfigParser()
XmlSystemConfigParser()
YamlSystemConfigParser()
抽象工厂:
public interface IConfigParserFactory {
IRuleConfigParser createRuleConfigParser();
ISystemConfigParser createSystemConfigParser();
//扩展分类方式
}
public class JsonConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleConfigParser() {
return new JsonRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemConfigParser() {
return new JsonSystemConfigParser();
}
}
public class XmlConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleConfigParser() {
return new XmlRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemConfigParser() {
return new XmlSystemConfigParser();
}
}
public class YamConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleConfigParser() {
return new YamRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemConfigParser() {
return new YamSystemConfigParser();
}
}
按照上面抽象工厂的方式,无论添加多少分类方式,这三个工厂类就可以满足。
DateFormat
:使用的是简单工厂,只需要提供DateStyle
和TimeStyle
,可快速得到一个DateFormat
对象。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale aLocale);
//使用
public static void main(String[] args) {
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT);
String now = format.format(new Date());
System.out.println(now);
}
参考文章:
简单工厂模式、工厂方法模式和抽象工厂模式有何区别?
极客时间《设计模式》(王争)