Effective Java -- 用静态工厂方法代替构造器

此系列文章为本人对《Effective Java》一书的学习笔记,主要是记录对书中重点内容的理解。
既然有缘看到此文,那么希望能对你有所帮助。
本文对应原书 第1条 用静态工厂方法代替构造器

获取类实例的方法:
  • 类的构造器
  • 静态工厂方法(注:其和设计模式中的工厂方法模式并不相同)

对于前者,我们相对熟悉,直接通过new关键字使用类的构造方法即可获取类的实例。
而后者,则是提供一个public的静态工厂方法(static factory method)以返回类的实例。

静态工厂方法的优势:
  • 静态工厂方法具有名称
    由于构造器的名称无法进行自定义,所以在多个构造器同时存在的情况下,容易出现混乱,只能通过调整参数顺序等方式加以区分,而这种方式,又容易让使用者迷惑,不知道到底应该使用哪一个构造器。
    而静态工厂方法则可以规避这一问题,可以通过自定义名称轻松突出方法与方法之间的区别,从而便捷用户的理解与选择。
// 构造器
public User(Long id, String name) {
	// 利用姓名实例化
	...
}

public User(String nickName, Long id) {
	// 利用昵称实例化
	...
}

// 静态工厂方法
public static User getUserByName(Long id, String name) {
	return new User(id, name);
}

public static User getUserByNickName(Long id, String nickName) {
	return new User(nickname, id);
}
  • 不必每次调用时创建一个新对象
    这一条非常重要,众所周知new关键字每次都会新生成一个对象,而静态工厂方法则可以为我们提供了单例的思路。
    我们可以在第一次调用时检查该类是否已存在实例,若存在即返回该实例,不存在则进行初始化,这样的操作使得性能、效率获得极大的提升(也正是Spring默认使用单例的原因)。
public class Pet {
	
	private static Pet INSTANCE;
	
	// 私有构造方法 确保安全
	private Pet() {
	}
	
	// 静态工厂方法
	public static Pet getInstance() {
		if (INSTANCE == null) {
			// 处理线程安全性
			synchronized (this) {
				if (INSTANCE == null) {
					INSTANCE = new Pet();
				}
			}
		}
		return INSTANCE;
	}
	
}
  • 可以返回原返回类型的任何子类型的对象
    构造方法只能返回所属类的实例对象,而静态方法,不仅可以返回自身的实例对象,还可以返回任何一个子类对象。
    这样我们在选择返回对象的类时,就有了更大的灵活性。
public class Pet{
	...
	public static Pet getPet() {
		return new Cat();
	}
}

public class Cat extends Pet {
	...
}

  • 所返回对象的类可以随着每次调用而发生变化
    返回对象的类并不是固定的,完全可以利用参数返回不同的类实例。
public class Pet{

	public static Pet getPet(String name) {
		Pet pet;
		switch (name) {
			case "Cat":
				pet = new Cat();
				break;
			case "Dog":
				pet = new Dog();
				break;
			default:
				pet = new Pet();
				break;
		}
		return pet;
	}
}

public class Cat extends Pet {
	...
}

public class Dog extends Pet {
	...
}
  • 方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
    还是以上述的宠物为例,你的静态工厂方法是提供一个Pet实例,可能在在最初的时候,什么品种也没有,但后来新增了几个品种,此时你的方法就可以返回这些新的品种。
public class Pet{

	// 存放品种
    private static final Map<String, Pet> map = new HashMap<>();

    // 根据名称获取宠物
    public static Pet getPet(String name) {
        return Optional.ofNullable(map.get(name)).orElseThrow(() -> new IllegalArgumentException("还没这个品种"));
    }

    // 后续增加品种,将其放入map
    public static void add (String name, Pet pet) {
        map.put(name, pet);
    }

    public static void main(String[] args) {
        try {
            getPet("Cat");
        }catch (Exception e) {
        	// 首次获取时由于map里没有Cat,会报错
            System.out.println(e.getMessage());
        }
        // 动态进行品种的添加
        add("Cat", new Cat());
        // 再次获取则可以得到猫的实例
        System.out.println(getPet("Cat"));
    }
}
静态工厂方法的劣势:
  • 类如果不含public或protected构造器,就不能被子类实例化
    在使用静态工厂方法时,由于经常会处于保护目的,将构造器私有化,由于子类实例化时需要调用父类的构造器,所以私有的构造器会导致类无法被继承。
    处理的办法是,用复合composition)取代继承,以实现类的正常拓展。

  • 难以被使用者(程序员)发现
    由于静态工厂方法没有强行的约束,具有较高的自由,这样的自由也使得它不容易被使用者所发现,所以在没有固定规范出台之前,遵循一些惯用名称会是一个比较好的方法。常用的名称有:

    from : 类型转换方法,它只有单个参数,返回该类型的一个相对应的实例;

    of : 聚合方法,带有多个参数,返沪该类型的一个实例,把他们合并起来;

    valueOf : fromof的一种替代方法;

    instance/getInstance : 返回的实例是通过方法的参数(如有)来描述;

    create/newInstance : 像instancegetInstance一样,但其能够确保每次都获得新的实例;

    getType : 像getInstance一样,但是在工厂方法处于不同的类中的时候使用;

    newType : 像newInstance一样,但是在工厂方法处于不同的类中的时候使用;

    type : getTypenewType的简化版;

总结

简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解他们各自的长处。
而静态工厂在实际项目中往往更加适合,因此你需要优先考虑到它。

水平有限,若文章中存在错误,恳请不吝赐教,这对我以及后面的读者都有重要意义;
若文章能够帮助到你,还望一键三连,你的支持,是我最大的动力。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值