工厂方法模式(Factory Method)
什么是工厂模式?官方有很多解释,我这里把我所理解的结合经验,诠释给大家,我不想绞尽脑汁,抽象总结出类似于古文(JAVA编程思想)那样难于理解的文字,也没那个水平言简意赅的、一针见血的总结出众生都能看懂的解释。只能笨鸟先飞、勤能补拙,从实践出真知的角度出发,抛砖引玉,供大家思考。公司有个这样一个需求,在App中要使用到LBS定位来实现某些功能。产品技术一大堆开始了需求、技术确认会,当大家讨论到定位是用百度API来实现,还是用高德来实现。大家争论不休,有人说百度定位不准,有人说高德定位不准,众说纷纭。咋办?最后,B总拍板,两个一起用,哪个好用哪个,领导拍板了,但说了又等于没说,咋办?工厂模式这时候就呼之欲出了,我两个都给你设计,代码设个开关和参数,你说用高德不爽,我改个参数,就换百度,直到领导高兴为止,于是代码就产生了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
public
class
test {
public
static
void
main(String[] args) {
Location position=
new
LocationFactory().getInstance(
"xiaomi"
);
position.getPosition();
position.getCityName(
10
,
20
);
}
}
class
LocationFactory{
public
static
Location getInstance(String type){
if
(
"baidu"
.equals(type)){
return
new
BaiduLocation();
}
else
{
return
new
XiaoMiLocation();
}
}
}
class
BaiduLocation
implements
Location{
@Override
public
void
getPosition() {
// TODO Auto-generated method stub
System.out.println(
"通过百度定位获取到当前的经纬度是XXXXX"
);
}
@Override
public
void
getCityName(
long
lat,
long
lng) {
// TODO Auto-generated method stub
System.out.println(
"通过百度定位获取到当前的城市是XXXXX"
);
}
}
class
XiaoMiLocation
implements
Location{
@Override
public
void
getPosition() {
// TODO Auto-generated method stub
System.out.println(
"通过小米定位获取到当前的经纬度是XXXXX"
);
}
@Override
public
void
getCityName(
long
lat,
long
lng) {
// TODO Auto-generated method stub
System.out.println(
"通过小米定位获取到当前的城市是XXXXX"
);
}
}
interface
Location{
public
void
getPosition();
public
void
getCityName(
long
lat,
long
lng);
}
|
上面的例子,较好的阐述了工厂模式的概念。LocationFactory是一个工厂类,静态函数getInstance的参数决定了是选用百度还是高德,这样,对于调用者来说,只需要关心你是用百度还是高德即可。Location是一个接口,它抽象出高德和百度常用的函数调用。拿定位来说,基本上常用的就是根据经纬度查询地址,或者定位当前所在位置获取经纬度。当然可能还有更多有用的函数,我这里就不在列举。有了这样一个共性的接口,XiaoMiLocation和BaiduLocation通过实现它的接口就能分别满足调用者的需求。调用者就能够任意通过改变参数,来实现来自不同定位API的需求。当然,如果百度和高德不爽,你完全可以使用谷歌API,只需要构造一个GoogleLocation类并实现Location接口的方法即可。
工厂模式的应用非常广泛,比如android的bitmap中常用的BitmapFactory类,创建Bitmap对象,通常使用静态工厂方法。
1.意图
定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方式模式使一个类的实例化延迟到其子类。
2.结构图和代码
我们先看看标准的工厂方法结构图:
先抽象的产品类,抽象的工厂类,然后用客户端具体的工厂生产相应的具体的产品,但是客户端并不知道具体的产品是怎么生产的,生产的过程封装在工厂里。所以说,某种程度上,工厂方法模式改变了我们直接用new创建对象的方式,一个很好的开始,意义重大。
以ThreadFactory为例:
这张图其实和原本的结构图有细微的区别,那就是参数化得工厂,而且从业务意义上也有些不同,但是思想是一样的。
我们来看下具体的代码:
1
2
3
4
5
6
7
8
9
|
//抽象产品
public
interface
Runnable {
public
abstract
void
run();
}
//抽象工厂
public
interface
ThreadFactory {
Thread newThread(Runnable r);
}
|
下面是具体的实现:
比如AsyncTask类中工厂的具体实现如下:
1
2
3
4
5
6
7
8
9
10
11
|
//工厂实现类
private
static
final
ThreadFactory sThreadFactory =
new
ThreadFactory() {
private
final
AtomicInteger mCount =
new
AtomicInteger(
1
);
public
Thread newThread(Runnable r) {
return
new
Thread(r,
"AsyncTask #"
+ mCount.getAndIncrement());
}
};
//那么产品类在哪里呢?
//做为参数Runnable r,我们可以创建千千万万个此系列的产品类
//同理,我们可以创建另外类似的工厂,生产某种专门的线程,非常容易扩展
|
看到这里,我们一方面为它的生产便利性感叹,一方面又为没创建某类产品都要创建一个工厂而感到繁琐,所以我们下面介绍简单工厂,它的结构图如下:
简单工厂把抽象工厂去掉了,你就创建一个专门生产某类产品就好。在一些特定而又不负责的领域非常实用方便套用这个模式。
在android中的Connection类中使用到了这个类:
其中Connection这个抽象类,既充当抽象产品类,也充当具体工厂类。
因为这种情况下,我们往往需要的是马上生产子类,getConnection方法往往是静态的,所以简单工厂,也叫静态工厂方法。
我们看看代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
abstract
class
Connection{
static
Connection getConnection(
Context context, HttpHost host, HttpHost proxy,
RequestFeeder requestFeeder) {
if
(host.getSchemeName().equals(
"http"
)) {
return
new
HttpConnection(context, host, requestFeeder);
}
// Otherwise, default to https
return
new
HttpsConnection(context, host, proxy, requestFeeder);
}
}
|
这就是简单工厂,一个很简单的参数化工厂,真的很简单。
3.效果
1. 创建型模式;
2.参数化工厂方法模式得到相应的对象;
3.为子类提供挂钩;
4.连接平行的类层次。