更多文章和资源欢迎访问:SuperCoder Blog
文章目录
前言
工厂模式是用工厂方法代替new操作的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量(降低耦合)。
一、简单工厂模式
简单工厂模式不属于GOF的23种经典设计模式,相当于一种编程习惯。
简单工厂模式包含如下三种角色:
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品:实现或者继承抽象产品的子类。
- 具体工厂:提供了创建产品的方法,使用者通过该方法来获取产品。
核心代码示例:
/**
* 创建一个抽象类:抽象子类的共有方法
* 创建子类实现父类的抽象方法
* 在工厂方法中根据类型创建不同的具体对象
**/
public class SimpleCoffeeFactory {
// 根据type判断类型,实例化并返回对应对象
public Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
}
}
工厂处理创建对象的细节,一旦有了工厂,后期如果需要对象直接从工厂中获取即可。这样也就解除了和实现类的耦合,但同时又产生了新的耦合。后期如果再添加新的类,就必须修改工厂类的代码,违反了开闭原则。
静态工厂模式:在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23种设计模式中的。
代码实例
public class SimpleCoffeeFactory {
public static Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffe;
}
}
二、工厂方法模式
针对简单工厂模式的缺点,使用工厂方法模式就可以完美的解决,完全遵循开闭原则。
工厂方法模式(FACTORY METHOD)是一种常用的类创建型设计模式,此模式的核心精神是封装类中变化的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽象工厂、具体工厂、抽象产品、具体产品。
四个角色:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
核心代码示例:
/**
* 抽象工厂
**/
public interface CoffeeFactory {
Coffee createCoffee();
}
/**
* 具体工厂
*
* 抽象产品为coffee,具体产品为LatteCoffee和AmericanCoffee
* 这种工厂模式可以通过不同的具体工厂创建出不同的具体产品
**/
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
从以上代码可以看到,要增加产品类时不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点,但要相应地增加工厂类。
工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
优点:
在获取对象时只需要知道具体工厂的名称就可以得到对应的对象,无须知道具体创建过程;在系统增加新的类时只需要添加对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
每增加一个类就要增加一个对应的具体工厂类,增加了系统的复杂度。
三、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)隶属于设计模式中的创建型模式,用于产品族的构建。抽象工厂是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂是指当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。
工厂模式中的每一个形态都是针对一定问题的解决方案,工厂方法针对的是多个产品系列结构;而抽象工厂模式针对的是多个产品族结构,一个产品族内有多个产品系列。
抽象工厂模式的主要角色如下:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
核心代码示例:
/**
* 抽象工厂
**/
public interface DessertFactory {
Coffee createCoffee();
Dessert createDessert();
}
/**
* 具体工厂
**/
public class AmericanDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
public Dessert createDessert() {
return new MatchaMousse();
}
}
public class ItalyDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
public Dessert createDessert() {
return new Tiramisu();
}
}
如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。
优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
四、模式扩展(简单工厂+配置文件解除耦合)
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接获取即可。
创建配置文件
american=cn.com.supercoder.pattern.AmericanCoffee
latte=cn.com.supercoder.pattern.LatteCoffee
代码示例:
public class CoffeeFactory {
private static Map<String,Coffee> map = new HashMap();
// 加载配置文件
static {
Properties p = new Properties();
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
p.load(is);
// 遍历Properties集合对象
Set<Object> keys = p.keySet();
for (Object key : keys) {
// 根据键获取值(全类名)
String className = p.getProperty((String) key);
// 获取Class对象
Class clazz = Class.forName(className);
// 实例化对象
Coffee obj = (Coffee) clazz.newInstance();
// 将对象以键值对的形式存入map
map.put((String)key,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取对象时直接根据配置文件中的key获取对应的对象
**/
public static Coffee createCoffee(String name) {
return map.get(name);
}
}