背景
关于工厂模式和建造者模式,是两个意思比较相近的概念。近期在项目中抽象逻辑层的遇到了这个问题,这里整理区分一下,以便在以后能够更好的使用。
本质上来讲,所有模式都是固定的套路,然而,总是先是熟练使用所有招数,然后才能无招胜有招吧。
工厂模式
顾名思义,工厂是用来生产东西的。自定义工厂,当然就是你让它生产什么,它就该生产什么。这里的什么,其实就是对象。
这样一来,其实这种模式是用来封装对象的创建的。这是非常重要的一个目的。(我认为至少有两个目的)
那么,为什么连创建一个对象的过程,都需要封装吗?答案是看你的使用场景和需求。
比如说一个Bitmap的构建就可以通过工厂来实现,它有一个BitmapFactory。Bitmap可以从本地图片、输入流、数组、文件等不同数据来源构建,BitmapFactory对于生产一个Bitmap是这样处理的:
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
...
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
...
public static Bitmap decodeByteArray(byte[] data, int offset, int length)
...
public static Bitmap decodeResource(Resources res, int id, Options opts)
...
当然,这样行不行呢?
public Bitmap(FileDescriptor fd, Rect outPadding, Options opts)
即,通过一个构造函数,来实现从不同数据来源生产。这样做我觉得最大的缺点,就是逻辑上不清晰。逻辑上的清晰,在复杂的项目结构中,非常重要。和上面对比,所以,在Android中,根本无法通过构造函数创建一个Bitmap对象。
我自己有时候,也会这么用:
class A extends Alpha
{}
class B extends Alpha
{}
class C extends Alpha
{}
class SimpleFactory
{
public static Alpha createProduct(String type)
{
if(type.equals("a"))
return new A();
else if(type.equals("b"))
return new B();
else if(type.equals("c"))
return new C();
else
retrun null;
}
}
就这么简单?
不,有些书上,甚至不认为它是一个工厂方法。它是封装了对象没错,但是,通常所谓模式,都是一种解耦合、增强代码灵活性的方法,上面所有方法,显然并没有体现到。
所以,这里应该还有工厂方法的第二个主要作用:体现代码的解耦能力。
思路是这样的:对于某人(创建者)来说,当需要生产一种新的商品(产品)的时候,它才会去创建这个工厂,让这个工厂来实现生产该商品。
现在来修改一下上面的Bitmap的构建过程,增加一点需求,即我们需要对Bitmap做一些管理。
Now,增加一个BitmapManager,作为创建者,当它发现需要一种通过流来创建Bitmap产品的方法时,增加一条生产线(创建一个工厂)来生产它就可以了。代码上大概修改成这样:
//BitmapFactory,抽象出来创建Bitmap的方法
public interface SimpleBitmapFactory{
Bitmap createBitmap();
}
//实际生产Bitmap的一个工厂
public class BitmapFromStreamFactory implements SimpleBitmapFactory{
public Bitmap createBitmap(){
......
}
}
//Bitmap创建者
public class BitmapManager{
private Bitmap bitmap ;
public Bitmap getBitmap(SimpleBitmapFactory simplebBitmapFactory){
Bitmap bitmap = simplebBitmapFactory.createBitmap();
bitmap...//此处可以对bitmap做一些设置
return bitmap ;
}
}
//实际使用过程中
bitmapManager.getBitmap(new BitmapFromStreamFactory ());
这个过程和上面BitmapFactory相比主要的差别在哪里?
这个过程更好的体现了设计模式的开闭原则(Open Close Principle)和接口隔离原则,即对于软件实体(类、模块等)关闭修改,只进行扩展。同时,各个实体的依赖关系,应该是通过接口解耦,而不是依赖具体实现。
遵循这些原则的妙用,在于当一个系统变得很复杂的时候,还能够保证代码逻辑的清晰,我们在更改需求或者功能的时候,能够减少工作量。同时,不至于发生这样的现象:我们为了需求更改了一个方法,但是该方法在另外一个隐蔽的地方也有调用,更改导致以前已经测试好的功能产生异常。
最后,我觉得有些时候,可能不会想到什么时候该使用这种工厂方法。这,大概就要看对需求的抽象能力和判断能力了。一动一静,静的地方不需要考虑太多,关键就在于这些“动”的地方。
建造者模式
建造者模式也是用来构建一个对象,但是它的侧重点在于基于蓝图,或者说知识,或者说构建流程,来创建出一个新的对象。
我们来看看OkHttp对象的构建过程,通常我们会这么使用:
okHttpClient = new OkHttpClient.Builder()
.readTimeout(10, SECONDS)
.writeTimeout(10, SECONDS)
.connectTimeout(10, SECONDS)
.cache(cache)
.pingInterval(5, SECONDS)
.sslSocketFactory(defaultSslSocketFactory)
.addInterceptor(new LoggingInterceptor())
.build();
我们build出来了一个OkHttpClient,但是构建的流程已经设置好了,如果我们需要自定义其中一部分的构建流程,在其中添加即可。这里的Builder是OkHttpClient的一个静态内部类。
build过程实际上如下:
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
有时候,我会这么用:
public abstract BaseClass{
public abstract initPageParams();
private void initMsg(){
...
}
private void initHandler(){
...
}
public BaseClass(){
initMsg();
initHandler();
initParams();
}
}
然后让具体实现类继承BaseClass。算是一种变体吧。
总结
好的代码,在前期,越早越好,需要充分考虑好动态和静态部分,做好抽象工作。好好运用好设计原则和设计模式,会为后来的道路减少很多不必要的麻烦。
ok,就到这里。不到之处,还请指出。
网友评论