考虑使用静态工厂方法替代构造方法

创建对象

构造方法创建对象

在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第一条

时间: 2024-10-01 20:13:19

考虑使用静态工厂方法替代构造方法的相关文章

考虑用静态工厂方法替代构造器的情况

考虑用静态工厂方法替代构造器 原来创建一个自定义类的实例最常用的方法是采用构造器,但一个类可以有多个构造器,而所有的构造器名字都相同,我们只能通过参数来区分不同构造器的作用.除了采用构造器外,还可以采用静态工厂方法,该方法返回类的实例便可,自定义的方法可以自己命名,这样一来就很容易区分每个方法的作用了. 例子: publicclass CreateObject { intage; String name; public CreateObject(String name){ this.name =

一、考虑使用静态工厂方法替代构造函数

1.何为静态工厂方法 静态工厂方法就是一个返回类实例的静态方法.比如Boolean的valueof方法: 1 public static final Boolean TRUE = new Boolean(true); 2 public static final Boolean FALSE = new Boolean(false); 3 4 public static Boolean valueOf(boolean b) { 5 return (b ? TRUE : FALSE); 6 } 2.为

经验法则:考虑用静态工厂方法代替公有构造方法

经验法则:考虑用静态工厂方法代替公有构造方法 一.引出静态工厂方法 对于java类而言,为了让使用者获取它自身的一个实例化对象,会有以下方法: 1.该类提供一个公有的构造方法.在这种情况下,程序可以通过多个“new 构造方法”语句来创建类的任意多个实例.但是每执行一条new语句,都会导致java虚拟机的堆区中产生一个新的对象. 2.该类提供一个公有的静态工厂方法(它只是一个简单的静态方法,返回的是类的一个实例,要区别于设计模式中的工厂方法模式).对于某些java平台库类或自己的工具类.参数化类,

&lt;创建和销毁对象&gt;经验法则——考虑用静态工厂方法代替公有构造方法

一.引出静态工厂方法 对于java类而言,为了让使用者获取它自身的一个实例化对象,会有以下方法: 1.该类提供一个公有的构造方法.在这种情况下,程序可以通过多个“new 构造方法”语句来创建类的任意多个实例.但是每执行一条new语句,都会导致java虚拟机的堆区中产生一个新的对象. 2.该类提供一个公有的静态工厂方法(它只是一个简单的静态方法,返回的是类的一个实例,要区别于设计模式中的工厂方法模式).对于某些java平台库类或自己的工具类.参数化类,需要进一步封装创建自身实例的细节,并且控制自身

静态工厂方法VS构造器

我之前已经介绍过关于构建者模式(Builder Pattern)的一些内容,它是一种很有用的模式用于实例化包含几个属性(可选的)的类,带来的好处是更容易读.写及维护客户端代码.今天,我将继续介绍对象创建技术. 在我看来,下面这个类是非常有用的例子.有一个RandomIntGenerator 类,产生随机的int类型的整数.如下所示: public class RandomIntGenerator { private final int min; private final int max; pu

Think in Java 静态工厂方法学习总结

静态工厂方法是一个返回类实例的静态方法.静态工厂方法的用于替代构造方法. 1.静态工厂方法的优点 如果一个构造方法的参数没有具体描述要返回的对象,那么使用一定意义名字的静态工厂方法使得该类更利于使用. 如果一个类有多个特征相同的构造方法,那么应该考虑用有特殊意义名字的静态工厂方法来代替构造方法. 静态工厂方法在调用的时候不要求一定要有类对象.对于一个非可变类,可以使用一个预先构造好的实例,或者实例已经缓存起来,避免创建重复对象. 静态工厂方法可以为重复的调用返回同一个对象.第一他可以使一个类确保

[Effective Java]考虑用静态工厂方法代替构造器

本文主要介绍如何使用静态工厂方法已经在那种场合来使用这种方式代替构造方法. 众所周知,对于类而言,我们为了获得一个类的实例对象,通常情况下会提供一个公有的(public) 的构造器.当然除了这种方法以外,我们还可以通过给类提供一个public的静态工厂方法(static factory method)的方式来完成,让它返回一个类的实例. 先看一个简单的Boolean的示例,这个示例将boolean基本类型值转换成一个Boolean对象的引用. public static Boolean valu

考虑用静态工厂方法代替构造器的场景

总结点,使用场景: a.当你尝试使用多个构造器,然后,每个构造器的区别是签名(参数类型或者参数顺序不同或者参数数量不同),那么,这个时候,可以考虑使用静态工厂方法来替代构造器.“如果构造器的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码也更易于阅读” Example: BigInteger.probablePrime()返回一个素数的整数 相比构造器Biginteger(int, int, Random)要直观 Car.smallCar()返回小车

第一条:考虑用静态工厂方法代替构造器

1.静态工厂方法:一种特殊归纳的类静态方法,用来产生类的一个实例(对象). 2.不可实例化的类:可以将构造方法的访问权限设为私有,并在构造方法中抛出异常来保证类不能被实例化. 3.final:被final修饰的类不可及被继承,被final修饰的变量就是一个常量,只能赋值一次. -----> 对于工具类的设计习惯:1要把类用final修饰:2要把构造方法设为私有,并在构造方法中跑出异常. -----> 4.