最近在啃《Effective Java》这本神书。简单记录一下,方便以后温习。
静态工厂方法是什么?直接从字面应该就很好理解:
工厂方法:大家应该都知道,就是构建实例的方法呗。(比如:“江南皮革厂”就是创建“皮包”这个对象的工厂)
静态方法:这还有解释的必要么.......
合起来就是静态的构建对象的方法呗。。哦了,这就是静态工厂方法。本文到此结束,感谢大家观看!
哈哈,开个玩笑。 虽然静态工厂方法是什么很好理解。可是静态工厂方法到底有什么用呢,我们又为什么要用它呢?
一般情况下,java中我们创建对象要写一个公有的构造函数,外部调用构造函数来创建一个对象。静态工厂方法就是与它相对应的。代码分别如下:
class People {
private String sex = "男";
private String appearance = "一般";
private String asset = "穷";//原则上来讲,默认数据要符合普遍情况。
//普通构造函数--无参
public People() {
}
//普通构造函数--有参
public People(String sex) {
this.sex = sex;
}
public People(String sex, String appearance) {
this.sex = sex;
this.appearance = appearance;
}
public People(String sex, String appearance, String asset) {
this.sex = sex;
this.appearance = appearance;
this.asset = asset;
}
//静态工厂方法
public static People createGirlfriend() {//程序员的基本操作,new一个女朋友。
People people = new People();
people.sex = "女";
people.appearance = "倾国倾城";
people.asset = "市中心十栋楼";
return people;
}
}
上面就是一个简单的静态工厂方法,其实本质上就是加了一层封装。有了这层封装自己可以操作和控制很多东西。
下面看看两者的区别:
我们在java中应该经常看到类似这种构造函数:
class People {
private String sex = "男";
private String appearance = "一般";
private String asset = "穷";
//普通构造函数--无参
public People() {
}
//普通构造函数--有参
public People(String sex) {
this.sex = sex;
}
public People(String sex, String appearance) {
this.sex = sex;
this.appearance = appearance;
}
public People(String sex, String appearance, String asset) {
this.sex = sex;
this.appearance = appearance;
this.asset = asset;
}
}
当然,官方文档的注释还是很完善和规范的。我们可以看到每个构造函数的用处和每个参数的作用。可是如果在多点参数呢,又如果是自己写方法呢。(有时候懒再少些两行注释~~后期维护两行泪~) 调用的时候是不是就会一脸懵逼?我该调用哪个?这个参数是什么?要传什么值?况且每次构建对象的时候都要点进去看注释也挺麻烦的。
为了解决上述问题,我们可以使用静态工厂方法。因为静态工厂方法是可以有名字的,这样构建对象时就非常直观。比如文章开始的第一个demo。通过静态工厂方法createGirlfriend()方法可以直接创建一个“People”的实例,同时我们可以直接从方法名看出我们创建出来的“people”实例是“女朋友”。是不是很方便易懂呢~~
这个应该很好理解,因为静态工厂方法是多一层封装的。所以在这里面我们可以自由控制返回的对象,可以全局只使用一个对象(单例)或者控制什么时候创建新的实例,什么时候使用缓存的实例。代码如下:
class People {
private String sex = "男";
private String appearance = "一般";
private String asset = "穷";
//静态工厂方法
public static People createGirlfriend() {
People people = new People();//这里是我们可控制的,所以你可以选择new新的对象或者使用缓存的已创建的对象
people.sex = "女";
people.appearance = "倾国倾城";
people.asset = "市中心十栋楼";
return people;
}
}
这一点也好理解,先看代码:
class People {
//静态工厂方法
public static People createChildren() {
Children people = new Children();
return people;
}
}
//People的子类
class Children extends People {
public Children() {
}
}
这个很好理解了,Children是People的子类。所以在静态工厂方法中可以直接返回Children的实例。
不过这里我们不得不提一下设计原则--里式替换原则:任何基类可以出现的地方,子类一定可以出现。 --百度百科
最通俗的讲,就是我们在写子类的时候要注意:可以拓展父类功能和属性,但是不能修改。(也就是对扩展开放,对修改关闭)
理解:静态工厂方法可以根据参数不同返回不同的子类对象。代码如下:
class People {
/**
* @param timeForSingle 单身时长
* @return 对象
*/
public static People createGirlfriend(int timeForSingle) {
if (timeForSingle < 5) {//根据所传参数,返回不同的子类对象。
EighteenBeauty eighteenBeauty = new EighteenBeauty();
return eighteenBeauty;
} else {//单身超过五年,看男生都感觉很清秀了呢~~
Man man = new Man();
return man;
}
}
}
//18岁年轻漂亮的小姑娘
class EighteenBeauty extends People {
public EighteenBeauty() {
}
}
//男人
class Man extends People {
public Man() {
}
}
OK,看代码就很好理解了。根据所传递参数不同返回了不同的子类对象。
重头戏到了,这是本篇唯一难点。这个概念理解起来倒是很简单:静态工厂方法返回对象所属的类,在编写包含该静态工厂方法时可以不存在。(好像是把概念又抄了一遍,不过单从字面实在没什么可解释的了~~)。
不过重点在于,怎么实现呢?
这里直接从 这种静态工厂方法最典型的实现--服务提供者框架 来探讨。
服务提供者框架包含四大组件:(概念不太好理解,可以直接先看下面的例子讲解,然后回过头来再看概念)
概念太拗口,上栗子讲解:
//四大组成之一:服务接口
public interface LoginService {//这是一个登录服务
public void login();
}
//四大组成之二:服务提供者接口
public interface Provider {//登录服务的提供者。通俗点说就是:通过这个newLoginService()可以获得一个服务。
public LoginService newLoginService();
}
/**
* 这是一个服务管理器,里面包含了四大组成中的三和四
* 解释:通过注册将 服务提供者 加入map,然后通过一个静态工厂方法 getService(String name) 返回不同的服务。
*/
public class ServiceManager {
private static final Map providers = new HashMap();//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();
}
}
OK,代码中注释的很清楚了。
思考下,这么做有什么好处呢??重点看一下上面demo中的 “四大组成之四:服务访问API ”
想一想,是不是以后想要增加服务时只需要实现服务提供者接口、服务接口,然后约定一个服务名就可以了?
如果类的构造方法是私有的,那么这个类就不能被继承了。这时候建议用复合代替继承实现类的拓展。
这时候就需要我们自我约束了,利用静态工厂方法时一定要遵循命名规范,下面是一些常用的静态工厂方法命名: