解耦合的简单工厂模式
虽然简单工厂模式分离了产品的创建者和消费者,有利于软件系统结构的优化,但是由于一切产品创建的业务逻辑都集中在一个工厂类中,导致了没有很高的内聚性,同时也违背了开闭原则。另外,简单工厂模式的方法一般都是静态的,而静态工厂方法让子类继承是可能被隐藏的,因此,简单工厂模式无法形成基于基类的继承树结构。
到了这里,其实又要想,不要过度的优化,不要为了使用设计模式而使用设计模式,如果是业务比较简单的场景,这样的简单工厂模式还是非常好用的。但无论如何,繁琐的if-else判断还是不太好,一旦判断条件稍微多点儿,if-else写起来就非常繁琐。
观察一些开源框架实现类似场景的代码,发现它们使用了 Java 的反射机制省去了判断的步骤,比之前的繁琐的 if-else 判断要好一些,如下代码。
public interface FruitE {
void get();
}
public class BananaE implements FruitE {
@Override
public void get() {
System.out.println("香蕉");
}
}
public class AppleE implements FruitE {
@Override
public void get() {
System.out.println("苹果");
}
}
// 新的工厂类
public class FruitFactoryFive {
//该方法通过调用获取到的类的 newInstance 方法,创建这个类的实例并进行返回。
// 由于 newInstance 方法返回类型为 Object,因此需要通过类型转换将返回值转换为 FruitE 类型。
// 由于存在实例化失败的可能性,该方法还抛出了 InstantiationException 异常。
public static FruitE getFruit(String type) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//返回该类的Class对象
Class fruit = Class.forName(type);
return (FruitE) fruit.newInstance();
}
}
// 客户端
private static void five() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
FruitE apple = FruitFactoryFive.getFruit("simpleFactory.five.AppleE");
FruitE banana = FruitFactoryFive.getFruit("simpleFactory.five.BananaE");
apple.get();
banana.get();
}
补充:forName 方法和 newInstance 方法
从 JVM 的角度看,使用 new 的时候,这个要 new 的类可以没有被 JVM 加载,但是使用 newInstance,就必须保证这个类已经加载且这个类已经链接,而完成这两个步骤的正是 Class 的静态方法 forName(......),该方法调用了启动类加载器(bootstrap加载器)去加载类(不初始化)。<br />Class 类的对象方法 newInstance 与静态方法 forName 实际上是把 new 关键字做的事情分解为了两步:<br />**1、加载某个类**<br />**2、初始化**<br />这样分步调用构造器的好处是显而易见的,因为它的粒度更细,所以程序可以在实例化类的时候获得更好的灵活性,催生一些降耦手段。<br />**事实上,Class 类的 newinstance 方法经常被各种框架使用,它是解耦合的利器之一,比如设计模式中最最常见的工厂模式。**<br />**当然,一些知名的开源框架使用了更高级的asm等字节码框架,能使反射操作的性能非常高效,并且还能修改已经编译的字节码,使得程序的灵活性变得很强。**
依托配置文件(注解)完全解耦
但是依然不完美—客户端缺少调用的灵活性,客户端必须传入严格对应类名的字符串,甚至还要包含完整的包名,才能实例化对应的类,稍微差一点儿,都会失败。故还是前面说的,可以变为反射的方式实现,而反射实现工厂类,对于客户端又显得调用上不方便。一些开源框架使用了配置文件或者注解解决了该问题。
String className = readConfig();
// 从配置文件中获得类的句柄
Class c = Class.forName(className);
factory = (FruitE)c.newInstance();
利用配置文件消灭了写死的产品类名称,无论产品怎么变化,代码不会再修改,甚至可以更换类的子类,只要他们继承该类(实现接口)就可以。<br />当然进一步就是自定义注解处理器,实现自己系统的注解
浙公网安备 33010602011771号