美文网首页JavaJava In Mind
使用静态工厂方法代替构造函数

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

作者: SevenLin1993 | 来源:发表于2019-10-15 22:51 被阅读0次

    技术公众号: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<String,EventHandler> 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 <K, V> HashMap<K, V> newHashMap() {
              return new HashMap<K, V>();
          }
      }
      //使用静态工厂方法
      Map<String,Object> map = Maps.newHashMap();
      //使用构造方法
      Map<String,Object> map = new HashMap<>();//JDK1.6之前不支持这种写法
      
    • 正如第一点的例子,静态工厂方法可以有自己的名称,可以有一些默认值设置,可以减少构建实例的参数,自然也会提高代码的简洁度

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

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

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

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

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

    总结

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

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

    相关文章

      网友评论

        本文标题:使用静态工厂方法代替构造函数

        本文链接:https://www.haomeiwen.com/subject/ribemctx.html