通常在创建类的实例时,类会提供一个公有的构造器,然后使用new关键字进行创建。但在某些情况下使用类提供的公有静态方法来获取实例则会更具有优势。
public class Factory{
private String name;
private String address;
private List<String> workers;
//无参构造器
public Factory(){}
//有参构造器
public Factory(String name,String address,List<String> workers){
this.name = name;
this.address = address;
this.works = workers;
}
//静态工厂方法
public static Factory newIntance(){
return new Factory();
}
}
优点
- 静态工厂方法与构造器不同的第一优势在于,它们有名字
由于语言特性JAVA中类的构造器方法与类同名,无法透过构造器名称来判断返回的实例,并且在有多个重载的构造器时尤甚。但静态工厂方法可以透过方法名称判断返回的实例。
public class Factory{
....
//有参构造器
public Factory(String name,List<String> workers){
this.name = name;
this.works = workers;
}
//有参构造器
public Factory(List<String> workers,String address){
this.address = address;
this.works = workers;
}
//静态工厂方法
public static Factory newIntanceByNameAndWorkers(String name,List<String> workers){
return new Factory(name,workers);
}
//静态工厂方法
public static Factory newIntanceByAddressAndWorkers(String address,List<String> workers){
return new Factory(workers,address);
}
- 第二个优势,不用每次被调用时都创建新对象
此优势主要用在单例模式上,调用者不关心调用的实例是否是新创建的实例还是旧的实例,此时可以使用静态工厂方法返回已缓存的实例,这样可减少创建实例所带来的内存开销。 - 第三个优势,可以返回原返回类型的子类
设计模式中的基本的原则之一——『里氏替换』原则,就是说子类应该能替换父类,并且子类可以扩展父类的功能,但不能改变父类原有的功能。
类的构造器只能返回类的本身类型的实例,而静态工厂可以根据需要返回任何它的子类型的实例。
public class Factory{
....
//静态工厂方法
public static Factory newSubIntance(){
return new FruitFactory ();
//return new FoodFactory ();
}
public class FruitFactory extend Factory {}
public class FoodFactory extend Factory {}
- 第四个优势 返回的对象的类可以随着每次调用而发生变化,这取决与静态工厂的参数
静态工厂方法可以根据参数不同返回不同的子类对象。
public class Factory{
....
//静态工厂方法
public static Factory newIntanceByType(int type){
if(type == 1){
return new FoodFactory ();
}
return new FruitFactory ();
}
...
- 第五个优势 静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
这里直接从这种静态工厂方法最典型的实现–服务提供者框架 来探讨。
服务提供者框架包含四大组件:
1.服务接口:这是服务提供者要去实现的接口
2.服务提供者接口:生成服务接口实例的工厂对象(就是用来生成服务接口的)(可选)
3.提供者注册API:服务者 提供服务者自身的实现
4.服务访问API:根据客户端指定的某种条件去实现对应的服务提供者
//四大组成之一:服务接口
public interface LoginService {//这是一个登录服务
public void login();
}
//四大组成之二:服务提供者接口
public interface Provider {//登录服务的提供者。通俗点说就是:通过这个newLoginService()可以获得一个服务。
public LoginService newLoginService();
}
/**
* 这是一个服务管理器,里面包含了四大组成中的三和四
* 解释:通过注册将 服务提供者 加入map,然后通过一个静态工厂方法 getService(String name) 返回不同的服务。
*/
public class ServiceManager {
private static final Map<String, Provider> providers = new HashMap<String, Provider>();//map,保存了注册的服务
private ServiceManager() {
}
//四大组成之三:提供者注册API (其实很简单,就是注册一下服务提供者)
public static void registerProvider(String name, Provider provider) {
providers.put(name, provider);
}
//四大组成之四:服务访问API (客户端只需要传递一个name参数,系统会去匹配服务提供者,然后提供服务) (静态工厂方法)
public static LoginService getService(String name) {
Provider provider = providers.get(name);
if (provider == null) {
throw new IllegalArgumentException("No provider registered with name=" + name);
}
return provider.newLoginService();
}
}
缺点
- 如果类不含公有的或者受保护的构造器时,就是能被子类化
如果类的构造方法是私有的,那么这个类就不能被继承了。这时候建议用复合代替继承实现类的拓展。 - 静态工厂方法实际上就是静态方法,如果命名不规范的话程序员很难发现他们
这时候就需要我们自我约束了,利用静态工厂方法时一定要遵循命名规范,下面是一些常用的静态工厂方法命名:
- from 类型转换方法,它只有单个参数,返回该类型的一个实例,并把它们合并起来。
- of 聚合方法,带有多个参数,返回该类型的一个实例,把他们合并起来。
- valueOf 比from和of更繁琐的一种替代方法。
instance或者getInstance 返回的实例是通过方法的(如有)参数来描述的,但是不能说与参数具有同样的值。 - create或者newInstance 像instance或者getInstance一样,但create或者newInstance能够确保每次调用都返回一个新的实例
- getType 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
- newType 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。
- type getType和newType的精简版。
总结
对于创建一个类的实例,通过new关键来实现是极其方便的,但对于不熟悉此类的开发者,常常通过new关键字泛滥的创建类实例,但这样容易导致性能、内存方面的问题,也经常会使得代码结构变得混乱。
因此在构建类时虽然无法控制调用者的行为,但可以通过增大对方法的控制来约束调用者,从而提升类调用的安全性和代码的合理性。
参考链接:https://blog.csdn.net/u014129886/article/details/89670049
https://blog.csdn.net/slw20010213/article/details/121771785