使用静态工厂方法代替构造函数

技术公众号:Java In Mind(Java_In_Mind),欢迎关注!

前言

我相信大部分都看过《Effective Java》这本Java神书,这本书在我学习Java的路上给了我帮助可以说是受益匪浅,书中第一篇就建议:考虑用静态工厂方法代替构造器。我自从看了之后就开始使用实践该建议,到现在已经基本偏爱静态工厂方法,这里我就谈谈使用以来的一些领悟。

使用静态工厂方法的优缺点

优点
  • 静态工厂方法有名称
  • 不必每次调用都创建一个新对象
  • 可以返回原类型的任何子类型的对象
  • 创建参数化类型实例的时候,它们使代码更加简洁
缺点
  • 类如果不含公有的或者受保护的构造器,就不能被子类化
  • 静态工厂方法与其他静态方法没有任何区别

静态工厂方法有名称

这点确实是很好地优势,静态工厂方法有自己的名称,也就意味着可以表达某种意思,或者某些特殊的实例,例如,在使用一些通用返回结果的时候,可以用类似如此的静态工厂方法来创建实例:

public class Result {
        private boolean success;
    private String message;
    private Object content;
  
    private Result(boolean success,String message,Object content){
            this.success = success;
            this.message = message;
            this.content = content;
    }
        
    public static Result ofSuccess(Object content){
            return new Result(true,"处理成功",content);
    }
    
    public static Result ofFail(String message){
      return new Result(false,message,null);
    }
  
    // getter and setter...
}
//使用
Result.ofSuccess(content);
Result.ofFail("处理失败,找不到该记录");

这样子,通过静态工厂方法就可以很好表达要构造的对象的目的,而调用方可以比较方便获取实例。

不必每次调用都创建一个新对象

这个其实很常见,如Boolean类中的几个valueOf静态工厂方法,Boolean有两个静态域:TRUEFALSE,这样就不用每次创建一个新对象:

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
}

其实,我们在使用单例的时候,通常获取单例的方法就是一个静态工厂方法,并且也是不用创建一个新的对象:

public class SingleTon {
    private static SingleTon singleTon = new SingleTon();
    
    private SingleTon() {
    }

    public static SingleTon getInstance() {
        return singleTon;
    }
}

可以返回原类型的任何子类型的对象

静态工厂方法返回的实例可以是该类型的子类,而这个子类可以在编写静态工厂方法时不存在,这也就意味着可以由其他方来实现,也意味着对拓展友好,还可以根据静态工厂方法的入参来选择不同的实现,非常灵活。

  • 在JDK中有个例子就是EnumSet,根据入参的枚举的枚举值量来决定使用哪种枚举实例。

  • 在使用JDBC的时候,DriveManager在获取连接前得先加载对应的实现驱动,这样就能把实现交给第三方来完成,自己只需提供上层的抽象框架

  • 日常编程中可以用于同一需求的不同实现,通过静态工厂方法选择实现,并且有利于后期拓展,例如事件处理:

    public abstract class EventHandler {
          private static Map handlers = new HashMap<>();
        //事件类型
          public abstract String type();
          //子类注册入口    
          public static void register(EventHandler eh){
              handlers.put(eh.type(),eh);
        }
          //获取不同的实现
          public static EventHandler getHandler(Event e){
              return handlers.get(e.getType())
        }
    }
    

创建参数化类型实例的时候,它们使代码更加简洁

静态工厂方法还有一个好处就是提高代码的简洁度,也使得代码更加易读:

  • 使用JDK的类型推断,不过这在现在的JDK已经可以直接推断了

    public class Maps {
        public static  HashMap newHashMap() {
            return new HashMap();
        }
    }
    //使用静态工厂方法
    Map map = Maps.newHashMap();
    //使用构造方法
    Map map = new HashMap<>();//JDK1.6之前不支持这种写法
    
  • 正如第一点的例子,静态工厂方法可以有自己的名称,可以有一些默认值设置,可以减少构建实例的参数,自然也会提高代码的简洁度

类如果不含公有的或者受保护的构造器,就不能被子类化

类不含有public或者protect的构造方法时,子类的构建需要使用到父类的构造器,这个时候静态工厂方法就不适合

静态工厂方法与其他静态方法没有任何区别

使用静态工厂方法的时候,如果该类包含其他的静态方法,那么这个时候对于开发编程是不友好的,特别是在方法比较多,命名比较混乱的时候,使用者不能一眼就知道该用哪个方法来构造自己想要的实例,所以在编写静态工厂方法的时候尽量使用大家习惯的、约定俗成的命名风格:

  • of
  • valueOf
  • getInstance / newInstance
  • getType / newType,其中Type为类型,如,EventHandler.getEventHandler()

总结

结合我个人的经验,我认为使用静态工厂方法的利大于弊:

  • 提高代码的可阅读性
  • 更加符合人类的正常思维方式,想要某个类的实例直接找某个类就能获取到,而不是通过new关键字
  • 能够根据不同的需求创建不同的实例,而不用让看代码的人推测new出来的对象然后set某些值去猜测某种意图,通常我们的做法是注释,但是使用静态工厂的方法命名不是更加简洁吗
  • 提高代码的拓展性 ,方便拓展,对修改友好,符合开闭原则

你可能感兴趣的:(使用静态工厂方法代替构造函数)