s186

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

解耦合的简单工厂模式

虽然简单工厂模式分离了产品的创建者和消费者,有利于软件系统结构的优化,但是由于一切产品创建的业务逻辑都集中在一个工厂类中,导致了没有很高的内聚性,同时也违背了开闭原则。另外,简单工厂模式的方法一般都是静态的,而静态工厂方法让子类继承是可能被隐藏的,因此,简单工厂模式无法形成基于基类的继承树结构。
到了这里,其实又要想,不要过度的优化,不要为了使用设计模式而使用设计模式,如果是业务比较简单的场景,这样的简单工厂模式还是非常好用的。但无论如何,繁琐的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 />当然进一步就是自定义注解处理器,实现自己系统的注解
posted on 2023-06-01 16:16  186的博客  阅读(36)  评论(0)    收藏  举报