一、创建型模式
23种设计模式可以分为创建型模式、结构型模式、行为型模式,今天主要介绍创建型模式中有关工厂的三个模式,分别是:
- 简单工厂模式(不属于23种设计模式)
- 工厂方法模式
- 抽象工厂模式
二、简单工厂模式
简单工厂模式,也称为静态工厂方法,因为其创建实例的方法通常是静态方法,该模式的”简单“体现在其只需一个简单参数,即可获得所需的对象的实例,并且实现了对象创建和使用的分离。其定义如下:
定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
该模式包含三个角色如下:
- 工厂角色
- 抽象产品角色
- 具体产品角色
类图如下:
classDiagram
Product <|-- ConcreteProductA
Product <|-- ConcreteProductB
class Factory{
+Product factoryMethod(String args)
}
此处因为静态工厂方法返回的是抽象产品,所以可以根据传入参数的不同,创建相对应的具体产品角色进行返回,这也是多态的体现。
代码实现
public abstract class Product{
public abstract void methodDiff();
}
public class ConcreteProductA extends Product{
public void methodDiff(){
//具体实现
}
}
public class ConcreteProductB extends Product{
public void methodDiff(){
//具体实现
}
}
public class Factory{
public static Product getProduct(String args){
Product product = null;
if(args.equals("A")){
product = new ConcreteProductA();
}else if(args.equals("B")){
product = new ConcreteProductB();
}
return product;
}
}
public class Client{
public static void main(String args){
Product p = Factory.getProduct("A");
}
}
上述代码实现可以根据传入参数的不同,实例化出不同的具体产品,但是上面的写法存在一个十分明显的缺陷————硬编码,代码编译完成之后,我突然想要从产品A,换成产品B,那就需要重新修改代码,将参数修改为”B“,显然,这违背了开闭原则。有没有好的解决方法呢?有,那就是参数配置化,我们可以读取配置文件来获取具体的参数,这样就可以很方便的切换希望获得的具体产品了。
缺点
我们将参数配置化后之后可以方便的切换不同产品,但是,如果出现这样一个需求,我们需要产品C了,此时即使修改配置文件,程序也无法帮我们获取一个产品C的实例,因为根本就不存在产品C类,要想满足需求,我们就不得不去修改静态工厂方法,添加新的参数与产品类的ifelse语句。
三、工厂方法模式
工厂方法模式是对简单工厂模式的延伸,同时弥补了简单工厂模式的缺陷,更好地符合开闭原则的要求,在增加新的具体产品对象时,不需要对已有系统做任何修改,其在原有的简单工厂模式上,对工厂角色进行了抽象,类图如下:
classDiagram
Product<|-- ConcreteProduct
Factory <|-- ConcreteFactory
Factory : +Product abstract factoryMethod()
class ConcreteFactory{
+Product factoryMethod();
}
具体实现
public abstract class Product{
public abstract void methodDiff();
}
public class ConcreteProduct extends Product{
public void methodDiff(){
//具体实现
}
}
public interface Factory{
Product factoryMethod();
}
public class ConcreteFactory implements Factory{
public Product factoryMethod(){
return new ConcreteProduct
}
}
public class Client{
public static void main(String args){
Factory fac = new ConcreteFactory();//此处可以通过配置文件和反射进行优化
fac.factoryMethod();
}
}
同样的,上述客户端代码中通过new创建对象的方式不够灵活,可以使用配置文件+反射,将具体的工厂类名称写入配置文件中,读取配置文件中的类名,通过反射完成实例化。
缺点
当系统中需要添加新的具体产品类时,还要同时提供与之对应的具体工厂类,系统中类的个数将成对增加。 为了解决此问题,抽象工厂模式应运而生,
四、抽象工厂模式
前面介绍了抽象工厂模式可以解决增加新产品时类的个数成对增加的问题,那么它是如何实现的呢?很简单,对产品进行分类。那么如何分类呢?从产品等级结构和产品族两个维度进行划分。举个例子,有如下两个品牌:
- 品牌:手机、平板、笔记本
- 苹果:iphone、ipad、macbook
- 华为:mate60,华为平板,matebook
横向观察,即为产品等级机构,纵向观察即为产品族。比如苹果这个品牌,有着各式各样的产品,构成了十分庞大的生态,iphone则是手机这个品类中的一个。
与工厂方法模式对比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建整个产品结构中所有的产品,比如苹果工厂,生产iphone、ipad、iwatch、macbokk。
类图
classDiagram
AbstractFactory <|-- Apple
AbstractFactory <|-- HUAWEI
AbstractFactory : +AbstractPhone createPhone()
AbstractFactory : +AbstractPC createPC()
AbstractPhone <|-- iphone
AbstractPhone <|-- mate60
AbstractPC <|-- macbook
AbstractPC <|-- matebook
对比工厂方法模式与抽象工厂模式
最核心的区别就是具体的工厂类中是否提供了返回多种产品的方法,比如苹果工厂,如果既提供了获取苹果手机的方法,又提供了获取苹果笔记本的方法,则是抽象工厂模式,若去掉提供笔记本的方法,则退化为工厂方法模式。
五、聊聊开源框架中的那些创建型模式
简单工厂模式的实际应用
- Apache Commons Codec:使用简单工厂模式来创建编码器和解码器
// 定义StringEncoder接口
public interface StringEncoder {
String encode(String source);
}
// 具体的编码器实现类
public class Base64Encoder implements StringEncoder {
@Override
public String encode(String source) {
// Base64 编码实现
return Base64.getEncoder().encodeToString(source.getBytes());
}
}
public class HexEncoder implements StringEncoder {
@Override
public String encode(String source) {
// Hex 编码实现
return DatatypeConverter.printHexBinary(source.getBytes());
}
}
// 简单工厂类
public class EncoderFactory {
public static StringEncoder createEncoder(String type) {
if (type.equals("base64")) {
return new Base64Encoder();
} else if (type.equals("hex")) {
return new HexEncoder();
}
throw new IllegalArgumentException("Unknown encoder type");
}
}
工厂方法模式的实际应用
- Spring中的BeanFactory
// 定义一个Bean接口
public interface BeanFactory {
Object getBean(String beanName);
}
// XmlBeanFactory 实现
public class XmlBeanFactory implements BeanFactory {
// 实现获取Bean的方法
@Override
public Object getBean(String beanName) {
// 从XML配置文件中读取并返回Bean实例
}
}
- Apache Commons Logging中LogFactory
// 定义一个Log接口
public interface Log {
void info(String message);
void error(String message);
}
// LogFactory 工厂类
public abstract class LogFactory {
public static Log getLog(Class<?> clazz) {
// 根据配置或其他条件返回具体的Log实现
}
}
// 一个具体的Log实现类
public class SimpleLog implements Log {
@Override
public void info(String message) {
// 简单实现
}
@Override
public void error(String message) {
// 简单实现
}
}
- Hibernate中的SessionFactory
// 定义一个Session接口
public interface Session {
void beginTransaction();
void save(Object entity);
void close();
}
// SessionFactory 工厂接口
public interface SessionFactory {
Session openSession();
}
// 一个具体的Session实现类
public class DefaultSession implements Session {
@Override
public void beginTransaction() {
// 开启事务
}
@Override
public void save(Object entity) {
// 保存实体
}
@Override
public void close() {
// 关闭会话
}
}
// 一个具体的SessionFactory实现类
public class DefaultSessionFactory implements SessionFactory {
@Override
public Session openSession() {
return new DefaultSession();
}
}
抽象工厂模式的具体应用
- Spring中的ApplicationContext
// 定义一个ApplicationContext接口
public interface ApplicationContext {
Object getBean(String beanName);
}
// 抽象类 AbstractApplicationContext 实现了部分逻辑
public abstract class AbstractApplicationContext implements ApplicationContext {
// 抽象工厂方法
protected abstract void refreshBeanFactory();
@Override
public Object getBean(String beanName) {
// 具体的子类实现 bean 获取逻辑
}
}
// 具体的实现类
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
@Override
protected void refreshBeanFactory() {
// 从 classpath 下的 XML 文件中读取并创建 bean
}
}
public class FileSystemXmlApplicationContext extends AbstractApplicationContext {
@Override
protected void refreshBeanFactory() {
// 从文件系统中的 XML 文件中读取并创建 bean
}
}