zoukankan      html  css  js  c++  java
  • 考虑使用静态工厂方法替代构造方法

    创建对象

    构造方法创建对象

    在Java中,创建对象常用的方法是通过公有构造方法创建;

    举个例子:如下,是Boolean类的一个构造方法,以及通过该构造方法创建一个Boolean对象;

        public Boolean(String s) {
            this(toBoolean(s));
        }
    Boolean bTrue = new Boolean("true");

    静态工厂方法创建对象

    其实,创建对象还有另外一种方法,通过公有静态工厂方法来创建对象,不过这种方法往往容易被程序员忽略;

    举个例子,如下是Boolean类的valueOf方法,以及通过该静态工厂方法返回的Boolean实例,注意,这里并没有创建Boolean实例对象,而是返回事先创建好的Boolean对象;

        public static Boolean valueOf(String s) {
            return toBoolean(s) ? TRUE : FALSE;
        }
    Boolean bTrue = Boolean.valueOf("true");

    注意区分静态工厂方法和工厂方法模式

    注意,这里的静态工厂方法与设计模式里的工厂方法模式不是一个概念:

    • 静态工厂方法通常指的是某个类里的静态方法,通过调用该静态方法可以得到属于该类的一个实例;
    • 工厂方法模式是一种设计模式,指的是让具体的工厂对象负责生产具体的产品对象,这里涉及多种工厂(类),多种对象(类),如内存工厂生产内存对象,CPU工厂生产CPU对象;

    不过,如果要说相似的话,静态工厂方法跟简单工厂模式倒有那么点像,不过区别也挺大,简单工厂模式里的静态工厂方法会创建各种不同的对象(不同类的实例),而静态工厂方法一般只创建属于该类的一个实例(包括子类);

    使用静态工厂方法的优势

    1、可读性更强

     假设我们需要写一个产生随即数的类RandomIntGenerator,该类有两个成员属性:最小值min和最大值max,

    假设我们的需求是需要创建三种类型的RandomIntGenerator对象,

    1、大于min,小于max;

    2、大于min 小于Integer.MAX_VALUE;

    3、大于Integer.MIN_VALUE 小于max

    如果我们不使用静态工厂方法,代码一般如下设计:

    class RandomIntGenerator
    {
        /**
         * 最小值
         */
        private int min = Integer.MIN_VALUE;
        /**
         * 最大值
         */
        private int max = Integer.MAX_VALUE;
    
        /**
         * 大于min 小于max
         * @param min
         * @param max
         */
        public RandomIntGenerator(int min, int max)
        {
            this.min = min;
            this.max = max;
        }
        
        /**
         * 大于min 小于Integer.MAX_VALUE
         */
        public RandomIntGenerator(int min)
        {
            this.min = min;
        }
    
    //    报错:Duplicate method RandomIntGenerator(int) in type RandomIntGenerator
    //    /**
    //     * 大于Integer.MIN_VALUE 小于max
    //     */
    //    public RandomIntGenerator(int max)
    //    {
    //        this.max = max;
    //    }
    }

    观察以上代码,我们发现,以上代码不仅可读性差(new RandomIntGenerator(1, 10)与new RandomIntGenerator(10),不查文档,不看注释很难知道其创建的对象的具体含义),而且在设计最后一个构造方法的时候,还报错,因为已经存在一个参数一致的工作方法了,提示重复定义;

    那么假设我们使用静态工厂方法会怎样呢,如下所示:

    class RandomIntGenerator
    {
        /**
         * 最小值
         */
        private int min = Integer.MIN_VALUE;
        /**
         * 最大值
         */
        private int max = Integer.MAX_VALUE;
    
        /**
         * 大于min 小于max
         * @param min
         * @param max
         */
        public RandomIntGenerator(int min, int max)
        {
            this.min = min;
            this.max = max;
        }
        /**
         * 大于min 小于max
         * @param min
         * @param max
         */
        public static RandomIntGenerator between(int min, int max)
        {
            return new RandomIntGenerator(min, max);
        }
        /**
         * 大于min 小于Integer.MAX_VALUE
         */
        public static RandomIntGenerator biggerThan(int min)
        {
            return new RandomIntGenerator(min, Integer.MAX_VALUE);
        }
    
        /**
         * 大于Integer.MIN_VALUE 小于max
         */
        public static RandomIntGenerator smallerThan(int max)
        {
            return new RandomIntGenerator(Integer.MIN_VALUE, max);
        }
    }

    成功满足需求:创建三种类型的RandomIntGenerator对象,而且创建对象的时候,代码可读性比使用构造方法强;

    2、调用的时候,不需要每次都创建一个新对象

    JDK中的Boolean类的valueOf方法可以很好的印证这个优势,在Boolean类中,有两个事先创建好的Boolean对象(True,False)

    public final class Boolean implements java.io.Serializable,
                                          Comparable<Boolean>
    {
        /**
         * The {@code Boolean} object corresponding to the primitive
         * value {@code true}.
         */
        public static final Boolean TRUE = new Boolean(true);
    
        /**
         * The {@code Boolean} object corresponding to the primitive
         * value {@code false}.
         */
        public static final Boolean FALSE = new Boolean(false);

    当我们调用Boolean.valueOf("true")方法时,返回的就是这两个实例的引用,这样可以避免创建不必要的对象,如果使用构造器的话,就达不到这种效果了;

        public static Boolean valueOf(String s) {
            return toBoolean(s) ? TRUE : FALSE;
        }

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

        //RedDog和BlackDog为Dog的子类
        public static Dog getInstance(){
            return new RedDog();//或者return new BlackDog();
        }

    4、代码更加简洁

    package tmp;
    
    
    class MyMap<K,V> {
        /**
         *
         */
        public MyMap()
        {
    
        }
    
        public static <K,V> MyMap<K,V> newInstance(){
            return new MyMap<K, V>();
    
        }
    }
    
    public class Main
    {
        public static void main(String[] args)
        {
            MyMap<String, String> map1 = new MyMap<String, String>();
    
            //更加简洁,不需要重复指明类型参数,可以自行推导出来
            MyMap<String, String> map2 = MyMap.newInstance();
        }
    
    
    }

    使用静态工厂方法的缺点

     1、如果类不含public或protect的构造方法,将不能被继承;

    如下类,不能被其它类继承;

    class MyMap<K,V> {
        /**
         *
         */
        private MyMap()
        {
    
        }
    
        public static <K,V> MyMap<K,V> newInstance(){
            return new MyMap<K, V>();
    
        }
    }

    2、与其它普通静态方法没有区别,没有明确的标识一个静态方法用于实例化类

     所以,一般一个静态工厂方法需要有详细的注释,遵守标准的命名,如使用getInstance、valueOf、newInstance等方法名;

     参考自effective java第一条

  • 相关阅读:
    Python网络编程(weekly summary1)
    Python网络编程(子进程的创建与处理、简单群聊工具)
    Python网络编程(epoll内核监听,多任务多进程)
    Python网络编程(http协议,IO多路复用、select内核监听)
    Python网络编程(socket模块、缓冲区、http协议)
    Python网络编程(OSI模型、网络协议、TCP)
    Linux(Ubuntu 命令大全)
    计算机中的进制和编码
    算法图解(选择排序)
    Python全栈 MySQL 数据库(SQL命令大全、MySQL 、Python调用)
  • 原文地址:https://www.cnblogs.com/chenpi/p/5981084.html
Copyright © 2011-2022 走看看