前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >静态工厂方法

静态工厂方法

作者头像
不蜇人的小蜜蜂
发布2020-03-31 11:52:09
4980
发布2020-03-31 11:52:09
举报
文章被收录于专栏:不蜇人的小蜜蜂

A:没有对象怎么办?

B:new一个不就好了~~~

我们知道,当需要一个对象时,如果当前没有此对象,那我们首先想到的是new一个对象出来。这或许已经是我们习以为常并脱口而出的,正如下所示:

代码语言:javascript
复制
Animal animal = new Animal();

直接new对象会调用当前类的构造方法(有参或者无参),这看起来很正常,但有些时候或许这样做会为以后的扩展不是很友好。举个例子:

代码语言:javascript
复制
public class Animal{
  private Integer age;
  private Integer weight;
  
  //1、默认构造器
  public Animal(){}
  //2、全参构造器
  public Animal(Integer age, Integer weight) {
    this.age = age;
    this.weight = weight;
  }
  //3、部分参数构造器
  public Animal(Integer age){
    this.age = age;
  }
  ///4、如下这个构造函数会报编译错误,因为在构造器中会关注于参数类型,
  ///而不关注具体的属性信息。
  //public Animal(Integer weight) {
  //  this.weight = weight;
 // }
}

如原先的Animal类只有1、2、3这3个构造器,但此时业务出现新需求,想直接创建一个只有weight属性信息的Animal对象。那我们如果直接添加4构造函数就会出现编译异常,那我们也许会想到这不是有默认的无参构造器吗?直接按照下面这样写不就得了?

代码语言:javascript
复制
Animal animal = new Animal();
animal.setWeight(1);
//return animal

首先肯定一点,这样写是没有问题的!但如果业务代码想要创建一个只包含weight属性的Animal对象的话就得重复上述的代码,那有没有更好的一种写法来避免这种问题呢?当然是有的,接下来来引入静态工厂方法

先来看下面这个代码:

代码语言:javascript
复制
@Data
public class Animal{
  private Integer age;
  private Integer weight;
  
  public static Animal createDefaultAnimal(){
    return new Animal();
  }
  public static Animal createAnimalWithWeightOnly(Integer weight) {
    Animal animal = new Animal();
    animal.setWeight(weight);
    return animal;
  }
}

细品一下就会发现,欸,那我要想只获取包含有weight属性的Animal对象只需要调用Animal的createAnimalWithWeightOnly静态方法就好了,就不用到处new了。

确实如此,而且你也会发现这样写的话很好理解,而且也很优雅。当然这只是抛砖引玉,只阐述了静态工厂方法的一个优点,看过《高效java》的读者都知道开篇就是说“

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

(滑稽一下:用大字表示对文章的引用),同时也阐述了具体的优点:

代码语言:javascript
复制
第一个优势:与构造器相比它们有名字
第二个优势:不用每次被调用时都创建新对象(可以回忆一下基本的构造单例对象的写法)
第三个优势:可以返回原返回类型的子类
第四个优势:在创建带泛型的实例时,能使代码变得简洁

除了这常规的优势,总结自己在工作中以及学习过程中吸取他人的好的习惯发现:使用静态工厂方法能够增强作者对当前类的控制力。当然这听起来有些抽象,下面举个例子:

代码语言:javascript
复制
public class Animal {
    private static final Integer DOG = 1;
    private static final Integer CAT = 2;
    private static final Integer PIG = 3;

    private Integer value;
    private Animal(Integer val) {
        this.value = val;
    }

    /**
     * 静态工厂方法 --直接采用Animal.createDog()获取Animal对象,而不用关心里面的具体实现,
     * 也避免了外界传参导致的数据异常问题
     * @return
     */
    public static Animal createDog(){
        return new Animal(DOG);
    }
    public static Animal createCat(){
        return new Animal(CAT);
    }
    public static Animal createPig(){
        return new Animal(PIG);
    }

}

假如我有个Animal类,那我的小伙伴想要利用我的Animal生成一个Dog对象,只需要调用Animal.createDog(),不用关心具体的参数值啥的。假如我没有按照上述的代码写,那我的小伙伴很可能按照如下代码去创建一个Dog:

代码语言:javascript
复制
//-1为非法值。
Animal dog = new Animal(-1);

这就导致了一定的危险性,至少在编译期间我的小伙伴是不知道自己已经对Animal(Dog)赋错值了,直到业务端报出IllegalArgumentException。

当然这只是一个特殊的场景,但我觉得这完全能说明一些问题。至少我是不希望我的小伙伴调用我的代码块出现任何问题。

除此之外,我们在做单元测试的时候很容易写出如下代码:

代码语言:javascript
复制
Animal animal = new Animal();
animal.setAge(111);
animal.setWeight(222);
animal.setHight(333);
///。。。一系列setter操作
//Assert.equal(animal,xxxx,"");

这样看起来无伤大雅,但如果需要多个不同的animal,那我就会写多个上述的代码,看起来繁琐又麻烦,一不留心还可能赋错值,同时其他地方若引用对象的时候还要拷贝一份代码过去。那如果按照下面的写法:

代码语言:javascript
复制
public class AnimalTest{
  public static Animal createDefaultAniaml(){
    return new Animal();
  }
  
  public static Animal createAniamlWithAge(){
    Animal animal = new Animal();
    animal.setAge(19);
    return animal;
  }
}

这样的话,若其他的测试类想要使用同样的测试对象,只需要调用静态方法即可,省去了大量的CV时间。

其实静态工厂方法也不是什么神秘的技术,它更多地是一种思想,一种模式,一种治理手段,像是Integer.valueOf(1),String.valueOf(),都是静态工厂方法的典型案例,深刻理解并将其运用到实际的项目中去,更好的治理我们的代码块,做到优雅、重用。借用《Effective Java》中的话:

代码语言:javascript
复制
简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处
静态工厂经常更 合适,因 切忌第一反应就是提供公有的构造器, 不先考虑静态工厂

这样我们离架构师又近了一步!!!

附录:针对java中有关静态工厂方法的一些通俗约定

代码语言:javascript
复制
1、from一一类型转换方法,它只有单个参数,返回该类型的一个相对应的实例,
2、of 聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来,例如
  Set<Rank> faceCa ds = EnumSet. of ([JACK , QUEEN, KING]);
3、valueOf一一比 from of 更烦琐的 种替代方法,例如
  Biginteger val = Biginteger.valueOf(Integer.MAX_VALUE);
4、instance 或者 get Instance -返回的实例是通过方法的(如有)参数来描述
的,但是不能说与参数具有 同样的值,例如
StackWalke luke = StackWalke .getinstance(options);
5、create 或者 newInstance一一才象 instance 或者 getinstaηce 样,但 create
或者 newInstance 能够确保每次调用都返回一 新的实例
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BeeFarm 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、考虑使用静态工厂方法替代构造方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档