因为工厂类这个东西用来生成产品。那么一般我们只留一个工厂类使用就好了。那我们来尝试一下结合单例模式的抽象工厂。
首先定义一个抽象工厂。这里抽象工厂的类图
产品系列代码
public interface Product {
public void makeSerivice();
}
public class AProduct implements Product {
@Override
public void makeSerivice() {
System.out.println("Aproduct executes an order");
}
}
public class Product implements Product {
@Override
public void makeSerivice() {
System.out.println("Bproduct executes an order");
}
}
工厂系列代码
public abstract class AbstractFactory {
public abstract Product makeProduct();
}
public class AconcreteFactory extends AbstractFactory{
@Override
public Product makeProduct() {
return new AProduct();
}
}
public class concreteFactory extends ABstractFactory{
@Override
public Product makeProduct() {
return new BProduct();
}
}
如图包括产品Product类系列和工厂Factory类系列。AFactory负责生产AProduct,BFactory负责生产BProduct。可以看到一般每个产品系只需要一个ConcreteFactory的实例。因此工厂通常实现为一个Singleton。
这是一个常见的单例模式的类图。
现在我们要把这个抽象工厂单例化。
根据类图,我们可以想到几种方案。
第一种:把单例模式放在抽象工厂中实现。如图
第二种:让单例模式类继承抽象工厂,然后具体工厂再继承单例模式类。
第三种:让每个具体工厂分别实现单例模式。
OK。下面我们开始对每个方案进行探讨。
①当实现第一种时发现了一个问题:实现单例模式的工厂类的方法getInstance()会尝试实例化这个工厂,但同时抽象工厂是抽象类所以无法实例化。所以第一种想法代码不可行。
public abstract class SingletonAbstractFactory {
/* AbstractFactory properties and other methods */
public static SingletonAbstractFactory getInstance(){
//getInstance()方法是静态方法
//Abstract class cannot be instantiated 抽象类无法实例化
//Non-static method 'newObj()' cannot be referenced from a static context
return instance;
}
}
②同样,当实现第二种时发现了一个问题:由于构造函数私有化导致子类的构造函数无法会隐式调用父类的构造函数,单例类不能被继承。
或者有人见到有人使用的单例构造器也可被protected关键字修饰。认为如果这样单例子类可以调用父类的构造函数。但是这是存在缺陷的做法。试想一下,一个Protected 构造器意味着你可以创造一个实例从单例类所在包的任意位置,那么这还是单例模式吗?如果有,那是抽象单例【下面会见到】
《Effective Java》 提及:Enforce the singleton property with a private
constructor 让单例类的构造函数私有化
③现在我们来实现第三种方案:
public class AconcreteFactory extends AbstractFactory {
private volatile static AconcreteFactory instance;
private AconcreteFactory() { }
public static AconcreteFactory getInstance(){
if (instance == null){
synchronized (AconcreteFactory.class){
if (instance == null){
instance = new AconcreteFactory();
}
}
}
return instance;
}
@Override
public Product makeProduct() {
return new AProduct();
}
}
这种方案是代码可行的,Gof的书提到一句话:一个具体的工厂通常是一个单件。
可是每当新增一个工厂类,我们必须写对应的单例模式。感觉有点代码重复会有点繁琐。
这里我们可以想到把单例模式抽象处理。把具体工厂的实例步骤委托给抽象单例处理。
抽象单例的代码
public abstract class AbstractSingleton<T> {
private final AtomicReference<T> atomicReference =
new AtomicReference<T>();
public T getInstance(){
T ret = atomicReference.get();
if (ret == null){
synchronized (this){
if (ret == null){
ret = newObj();
atomicReference.set(ret);
}else {
ret = atomicReference.get();
}
}
}
return ret;
}
protected abstract T newObj();//这里用Protect修饰
}
传入一个工厂类给AbstractSingleton,通过AtomicReference保存一个传入类的引用,并且newObj()方法会延迟到子类中实现。当调用它的getInstance()方法时会单例化该工厂或者直接返回该工厂单例, 从而得到该具体工厂单例。
具体工厂实现代码如下:
public class AConcreteFactory extends AbstractFactory {
private AConcreteFactory(){}
private static final AbstractSingleton<AConcreteFactory> objHolder = new AbstractSingleton<AConcreteFactory>() {
@Override
protected AConcreteFactory newObj() {
return new AConcreteFactory();
}
};
@Override
public AProduct makeProduct() {
return new AProduct();
}
public static AConcreteFactory getInstance(){
return objHolder.getInstance();
}
}
在这里,具体工厂组合了抽象单例的引用,同时实现了抽象单例的抽象方法。
public class Main {
public static void main(String[] args) {
AConcreteFactory aConcreteFactory = AConcreteFactory.getInstance();
AProduct aProduct = aConcreteFactory.makeProduct();
aProduct.makeSerivice();//打印出“Aproduct executes an order”
AConcreteFactory aConcreteFactory1 = AConcreteFactory.getInstance();
System.out.println(aConcreteFactory==aConcreteFactory1);//true
}
}
如此,抽象工厂的子类工厂代码部分得到简化,同时抽象单例也分担了子类工厂单例化的职责。
【完】
注:以上讨论的设计模式基于Java,基于C++的代码可以看这里。大家要是有新的想法欢迎交流与分享。
参考资料:
https://stackoverflow.com/questions/16270353/extending-a-singleton-in-java
https://stackoverflow.com/questions/12661405/singleton-abstract-factory-pattern
https://stackoverflow.com/questions/20105914/how-do-you-combine-abstract-factory-with-singleton-pattern
https://stackoverflow.com/questions/16843323/c-object-equality
https://blog.youkuaiyun.com/zhmt/article/details/50804215?_t_t_t=0.5770868656024127