当EffectiveJava遇见Guava - 静态工厂方法代替构造器(规则1)


Effective Java中指出,使用静态工厂方法代替构造器有几大优势:

第一大优势 - 他们有名称。

多个构造器只能通过匹配参数类型的顺序不同来区分使用哪一个,这样常常会导致用户调用错误构造器,而静态工程方法则不同,可以通过方法名清晰的指明用意。

//本例只用来说明第一大优势,请不要纠结其它问题 
public class Foo {
Set<Bar> bars;
List<Car> cars;
//构造器1
private Foo(Set<Bar> bars) {
this.bars = bars;
}
//构造器2
private Foo(List<Car> cars) {
this.cars = cars;
}
//构造器3
private Foo(Set<Bar> bars, List<Car> cars) {
this.bars = bars;
this.cars = cars;
}
//静态工厂方法1
public static Foo newInstanceByBar(){
return new Foo(new HashSet<Bar>());
}
//静态工厂方法2
public static Foo newInstanceByCar(){
return new Foo(new ArrayList<Car>());
}
//静态工厂方法3
public static Foo newInstanceByAll(){
return new Foo(new HashSet<Bar>(),new ArrayList<Car>());
}
public static void main(String[] args) {
// 通过构造器创建实例,不好区分容易使用错误
Foo fbar = new Foo(new HashSet<Bar>());
Foo fcar = new Foo(new ArrayList<Car>());
Foo fall = new Foo(new HashSet<Bar>(),new ArrayList<Car>());
// 通过静态工厂方法可以清晰的用方法名识别
Foo fbar_static = Foo.newInstanceByBar();
Foo fcar_static = Foo.newInstanceByCar();
Foo fall_static = Foo.newInstanceByAll();
}
}
class Bar {}
class Car {}

对于Guava,并没有提供创建静态工厂方法的工具,但整个Guava API到处都是静态方法的实现,我们以Guava Collections Framewrok举例说明。

Guava对于第一大优势有很多实现:

List<Type> exactly100 = Lists.newArrayListWithCapacity(100);
List<Type> approx100 = Lists.newArrayListWithExpectedSize(100);
Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);

第二大优势 - 不必在每次调用他们的时候都创建一个新对象。

方便对象重用,还可以确保不可变的不会存在两个相等的实例,如果a==b那么a.equals.(b)才会返回true ,如果能保证这一点,就可以使用==操作符来比较对象,会有很大的性能提升。

第三大优势 - 他们可以返回原返回类型的任何子类型的对象。

这是一个非常强大的特性, Effective Java中列举了API、SPI、服务提供框架的关系来说明:

API(Service Interface): 服务公共接口 SPI(Service Provider Interface): 服务提供商接口 SPF(Service Provider Framework): 服务提供框架

看例子:

// 服务提供框架示意模型 - 服务API 
public interface ServiceAPI {
// 这里是服务指定的方法
}
// 服务提供框架示意模型 - 服务SPI
public interface ServiceSPI {
ServiceAPI newService();
}
// 服务提供框架示意模型实现
// 不可实例化的类,用来注册创建和提供访问
public class ServiceFramework {
private ServiceFramework() {
}// 强制防止实例化(规则4)

// 映射服务名到服务
private static final ConcurrentMap<String, ServiceSPI> spis = new MapMaker().makeMap();//使用Guava创建
public static final String DEFAULT_SPI_NAME = "<def>";

// 默认SPI注册API
public static void registerDefaultSPI(ServiceSPI spi) {
registerSPI(DEFAULT_SPI_NAME, spi);
}

// 指定SPI注册API
public static void registerSPI(String name, ServiceSPI spi) {
spis.put(name, spi);
}

// 服务访问API
public static ServiceAPI newInstance() {
return newInstance(DEFAULT_SPI_NAME);
}
public static ServiceAPI newInstance(String name) {
ServiceSPI spi = spis.get(name);
if(spi == null)
throw new IllegalArgumentException(
"No provider registered with name: " + name);
return spi.newService();
}
}
Note
静态工程方法返回的对象所属的类,在编写这个包含静态工厂方法的类时可以不必存在。上面的例子在编写ServiceFramework类时,ServiceAPI的实现类并不存在。这大大增加了框架的灵活性。

现在编写客户端测试程序

// 简单的服务提供框架测试程序 
public class Test {
public static void main(String[] args) {
// 服务提供商执行下面的注册
ServiceFramework.registerDefaultSPI(DEFAULT_PROVIDER);
ServiceFramework.registerSPI("comp", COMP_PROVIDER);
ServiceFramework.registerSPI("armed", ARMED_PROVIDER);
// 客户端执行下面的创建
ServiceAPI s1 = ServiceFramework.newInstance();
ServiceAPI s2 = ServiceFramework.newInstance("comp");
ServiceAPI s3 = ServiceFramework.newInstance("armed");
System.out.printf("%s, %s, %s%n", s1, s2, s3);
}
private static ServiceSPI DEFAULT_PROVIDER = new ServiceSPI() {
public ServiceAPI newService() {
return new ServiceAPI() {
@Override
public String toString() {
return "默认服务";
}
};
}
};
private static ServiceSPI COMP_PROVIDER = new ServiceSPI() {
public ServiceAPI newService() {
return new ServiceAPI() {
@Override
public String toString() {
return "Complementary 服务";
}
};
}
};
private static ServiceSPI ARMED_PROVIDER = new ServiceSPI() {
public ServiceAPI newService() {
return new ServiceAPI() {
@Override
public String toString() {
return "Armed 服务";
}
};
}
};
}

//输出如下 , Complementary , Armed

第四大优势 - 在创建参数化类型实例的时候,他们使代码变得更加简洁。

在JDK7之前,我们创建一个Collections大致是这么做的:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();

JDK7发布以后,我们可以简化成这样:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();

但是Guava还是宁愿使用静态工程方法,因为真的非常方便:

Set<Type> copySet = Sets.newHashSet(elements); 
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");

静态工程方法的缺点

  • 类如果不含公有的或者受保护的构造器,就不能被子类化,这也许会因祸得福,因为它鼓励开发人员使用复合,而不是继承。

  • 他们与其他的静态方法实际上没有任何区别 如果API文档没有明确的说明这是一个静态工程方法,就会很难识别出来。遵循标准的命名规范习惯,可以弥补这一劣势,下面列出一些惯用命名:

    • valueOf - 这样的静态工厂方法实际上是类型转换

    • of - valueOf的简洁方式

    • getInstance - 返回实例通过方法参数描述,对于单例,该方法没有参数,并返回唯一的实例

    • newInstance - 与getInstance不同的是,它返回的实例与所有其它实例都是不同的

    • getType - 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示i返回对象类型

    • newType - 像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示i返回对象类型

2013-05-29

posted on 2013-05-30 17:09 kuuyee 阅读(3875) 评论(1)  编辑  收藏 所属分类: JEE

评论

# re: 当EffectiveJava遇见Guava - 静态工厂方法代替构造器(规则1) 2013-05-31 12:41 11

发现了:

The project was not built since its build path is incomplete.

Cannot find the class file for com.aaap.workflow.engine.WorkFlowSupportSes. Fix the build path then try building this project

The type com.aaap.workflow.services.ForwardNodesFacadeSes cannot be resolved. It is indirectly referenced from required .class

意思是“工程需要用的包没有引导入完全,没有找到需要的类文件,请修改buildPath后重新编译项目”

和同事一比对,果然少引入了若干包,引入缺少的几个包后,重新编译,Problems视图里提示的“”信息没了。

现在勾选了重新编译,再修改,保存,编译一闪而过~~ 正常啦!!
  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

<2013年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

统计

随笔分类(139)

Linux内核

搜索

积分与排名

最新评论

阅读排行榜