一、介绍
所有的工厂类都是一个目的:降低具体产品与调用者(比如说客户端)的耦合程度。对调用者隐藏产品的构造和变化(包括类名等)
举一个实际的例子,来证明工厂模式的应用场景。
public class Product {
public void template(){//模板函数——不变
//do something......
hook_method();
}
public void hook_method();//卡榫函数——变
}
public class ProductA extends Product{
public void hook_method(){
//do something.....
}
}
public class ProductB extends Product{
public void hook_method(){
//do something.....
}
}
public class ProductC extends Product{
public void hook_method(){
//do something.....
}
}
public class Client{
public void main(String[] arg){
Product proA = new ProductA();
Product proB = new ProductB();
Product proC = new ProductC();
//.....如果说后面有一百个不同的Product,当然实际情况是,在不同的地方都会用到 new ProductA\B\C这样的具体字眼。
proA.template();
proB.template();
proC.template();
}
}
考虑下面的问题:
1、如果说这个时候,需求变了,要你把产品的名字改了,OMGD。可以想象,你需要改多少个 new ProductX(( ▼-▼ ))。
2、更加实际的情况是,有些产品是需要量产和包装的,它们的生产都是【标准化】的,因此如果构造这样的对象较多的话,客户端与具体的产品的耦合度就大大增加,同时,客户端承担的工作也过多。
使用工厂模式解决上面的困难
(1)简单工厂模式
UML结构:
设计思想:简单工厂模式又叫静态工厂模式,在工厂模式系列中属于抽象层次最低的,也是最容易实现的。它是根据客户端的要求(传入的参数、调用的方法)来返回所需要的对象,能够很方便的实现客户端与具体产品之间的解耦。
- 优缺点:
- 优点:结构简单,调用者只需要知道工厂和目标类别就可以。
- 缺点:违反了开闭原则,如果添加产品类别的时候需要修改工厂代码。
- 代码范例:
模板模式+简单工厂
public class Product {
public void template(){//模板函数——不变
//do something......
hook_method();
}
public void hook_method();//卡榫函数——变
}
public class ProductA extends Product{
public void hook_method(){
//do something.....
}
}
public class ProductB extends Product{
public void hook_method(){
//do something.....
}
}
public class ProductC extends Product{
public void hook_method(){
//do something.....
}
}
//简单工厂+模板模式
public class SimpleFactory{
public static int TYPE_A = 1;//产品类别
public static int TYPE_B = 2;
public static int TYPE_C = 3;
public Product pd; // 产品的引用
public Product createProduct(int type){
switch(type){
case TYPE_A:
pd = new ProductA();
break;
case TYPE_B:
pd = new ProductB();
break;
case TYPE_C:
pd = new ProductC();
break;
}
pd.function();//调用模板初始化,包装对象,对象的具体包装留给具体类别去实现
return pd;
}
public void function(){
pd.template(); //模板模式,隐藏了具体实现,解除了工厂与对象具体包装的耦合
}
}
public class Client{
public void main(String[] arg){
SimpleFactory fac = new SimpleFactory();
Product proA = fac.createProduct(SimpleFactory.TYPE_A);
Product proB = fac.createProduct(SimpleFactory.TYPE_B);
Product proC = fac.createProduct(SimpleFactory.TYPE_C);
//这个时候即使改变了产品的类名和实现,那么客户端也不需要知道,解除了客户端和具体产品的耦合
//但是增加产品的时候,需要修改工厂类,违反了开闭原则,容易出错。
}
}
(2)工厂模式
UML结构:
设计思想:把对象的创建延迟到子类实现,不同子类负责实现不同对象的创建,父类工厂负责将对象返回给外部调用者。对外部隐藏了具体的类别;主要是针对类别单一的产品。
- 优缺点:
- 优点: 遵循开闭原则,如果添加产品类别则只需要添加继承类就可以,不需要修改工厂类的代码。
- 缺点: 客户端需要知道具体的工厂名才能创建相应的对象;在某个产品有多个系列的时候,抽象工厂并不知道该创建哪个类别。
- 代码范例:
public class Product {
public void template(){//模板函数——不变
//do something......
hook_method();
}
public void hook_method();//卡榫函数——变
}
public class ProductA extends Product{
public void hook_method(){
//do something.....
}
}
public class ProductB extends Product{
public void hook_method(){
//do something.....
}
}
public class ProductC extends Product{
public void hook_method(){
//do something.....
}
}
//父类工厂
public class ProductFactory{
public abstract Product createProduct();
}
//具体子类工厂+模板模式
public class ProductAFactory{
public Product createProduct(){
Product pd = new ProductC();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
}
public class ProductBFactory{
public Product createProduct(){
Product pd = new ProductC();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
}
public class ProductCFactory{
public Product createProduct(){
Product pd = new ProductC();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
}
public class Client{
SimpleFactory facA = new ProductAFactory();
SimpleFactory facB = new ProductBFactory();
SimpleFactory facC = new ProductCFactory();
//此处把具体产品的创建细节与客户端解耦
Product proA = facA.createProduct(SimpleFactory.TYPE_A);
Product proB = facB.createProduct(SimpleFactory.TYPE_B);
Product proC = facC.createProduct(SimpleFactory.TYPE_C);
//这个时候即使改变了产品的类名和实现,那么客户端也不需要知道,解除了客户端和具体产品的耦合
//但是产品类别较多时,如果A、B、C下面各有两个风格时,那么工厂就不知道该创建哪一种了,这时候就需要用到抽象工厂模式了。
}
}
(3)抽象工厂模式
UML结构:
设计思想:工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结构。当产品有多个等级结构或者多个系列的时候,就需要用到抽象工厂模式,使得客户端在不知道具体产品名的情况下创建具体的产品【不知而用】。
优缺点:
优点:在产品种类和系列比较多的情况下,行形成了一个产品等级结构时,可以将复杂的产品内部结构隐藏,简化客户端的操作。
缺点: 当需要增加一个产品的时候,有多处需要添加新类;同时,如果对产品增加某个系列时需要修改原来的工厂类,违反开闭原则。
代码范例:
假设有C、D两个不同的产品,产品下面又分为两种不同风格,比 如:StyleC1、StyleC2、StyleD1、StyleD2。
这时候结构就需要改变了,抽象工厂有两个模板方法(createC、createD),负责生产StyleC和StyleD,但是抽象工厂本身是不知道是哪个类型的风格。具体风格的创建延迟给两个子类工厂FactoryStyle1和FactoryStyle2负责。
//不同类别产品父类
public class ProductA extends Product{
public void teplate(){ //模板函数——不变
//do something.....
hook_method();
}
public void hook_method();//卡榫函数——变化
}
public class ProductB extends Product{
public void teplate(){ //模板函数——不变
//do something.....
hook_method();
}
public void hook_method();//卡榫函数——变化
}
//不同风格产品
public class ProductSytleA1 extends ProductA{
public void hook_method(){
//do something.....
}
}
public class ProductStyleB1 extends ProductB{
public void hook_method(){
//do something.....
}
}
public class ProductSytleA2 extends ProductA{
public void hook_method(){
//do something.....
}
}
public class ProductStyleB2 extends ProductB{
public void hook_method(){
//do something.....
}
}
//抽象父类工厂
public class ProductFactory{
public abstract ProductA createA();
public abstract ProductB createB();
public abstract ProductC createC();
}
//具体子类工厂+模板模式
public class FactoryStyle1{
public ProductA createA(){
ProductA pd = new ProductStyleA1();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
public ProductB createB(){
ProductB pd = new ProductStyleB1();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
public ProductC createC(){
ProductC pd = new ProductStyleC1();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
}
public class FactoryStyle2{
public ProductA createA(){
ProductA pd = new ProductStyleA2();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
public ProductB createB(){
ProductB pd = new ProductStyleB2();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
public ProductC createC(){
ProductC pd = new ProductStyleC2();
pd.template(); // 模板方法,初始化和装配对象
return pd;
}
}
//客户端
public class Client{
public void main(String[] arg){
//生产Style1类型的产品
Factory fac1 = new FactoryStyle1();
ProductA proA = fac1.createA();//此时解除了具体产品与客户端的耦合。
ProductB proB1 = fac1.createB();
//生产Style2类型产品
Factory fac2 = new FactoryStyle2();
proA = fac2.createA();//客户端根本不知道此时风格已经变了。
proB = fac2.createB();
//如果需要更多的类型,则只需要知道工厂就可以了
//如果需要改变产品的构造和名字,也无需通知客户端
//抽象工厂模式类别层次结构复杂,所以适合在产品层次较深、类别较多的情况
}
}
二、Android综合运用范例
还是之前的Mp3播放器,在Android之中客户端(Activity)与播放器(MP3Player)之间解耦就是用到了工厂模式。这时候Service就充当了Factory的角色。当然里面还有Template模式和Observer模式。
UML图示:
源代码
参见Android框架设计模式(一)——Template Method
分析
这里面,Service除了是单独运行在后台的独立线程之外,它还是充当了抽象工厂的角色,具体工厂是我们自己继承实现的mp3Service_Factory:
public class mp3Service_Factory extends Service {
private IBinder mBinder = null;
@Override public void onCreate() {
//此处就生成了MP3Player对象
mBinder = new mp3Player(getApplicationContext());
}
//该函数就是Service与子类的卡榫函数
@Override public IBinder onBind(Intent intent) {
return mBinder; //返回对象
}
}
结合UML图看,如果我们现在还有电影、下载的需求,那么我们又可以继承IBinder类来实现不同产品,继承Service实现不同的装配工厂。然而客户端呢,永远就只需要知道IBinder和具体工厂便足够。
分解一下具体的步骤:
1、我们可以看到,客户端调用startService()来启动服务,随后就启动了Service抽象类反向呼叫了mp3Service_Factory的onCreate()函数,诞生了mp3Player产品对象。然后执行bindService()函数,将mConnection这个回调类(CallBack)与Service绑定。
public void onCreate(Bundle icicle) { super.onCreate(icicle);
//do initial
//开启服务
startService(new Intent(this, mp3Service_Factory.class));
//将客户端与服务进行绑定,通过mConnection中的卡榫函数回调来返回Service工厂所创建的MP3Player对象。
bindService(new Intent(Client.this,mp3Service_Factory.class), mConnection, Context.BIND_AUTO_CREATE);
}
2、创建了对象之后,Service抽象类反向呼叫了mp3Factory子类的onBind()卡榫函数返回了mp3Player对象。然后Service抽象类再用mConnection回调onServiceConnected()函数:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder ibinder) {
ib = ibinder;
}
public void onServiceDisconnected(ComponentName className) {}
};
将 创建好的MP3Player对象返回给客户端的ib(只是抽象IBinder,并没有涉及具体类名)。
3、客户端获取到了mp3Player对象后,调用IBinder里面的transact()函数反向呼叫到mp3Player的onTransact()函数,从而实现音乐的播放。
//卡榫函数,接通mp3Player与父类IBinder
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (1 == code){
this.play();
}else if ( 2 == code){
this.stop();
}
return true;
}
4、请留意,从始至终,客户端只知道IBinder对象而已,并不知道具体的mp3Player,也就是说如果这个时候mp3Player改变类名、构造等客户端都不知道的,即使换成mp4Player客户端也是不知道的,只需要实现一个mp4的工厂就可以了。
三、总结
其实上面的onServiceConnect()回调还是Observer模式,这个模式实现了客户端与后台服务的异步通信,可以让后台服务在准备好的时候通知客户端(在这里即返回mp3Player对象)。
总的来说,工厂模式是用来解决以下问题的:
(1) 对调用者隐藏具体的产品细节,调用者只需要告诉工厂需要生产什么样(磨具)的产品,而不需要知道具体产品的细节(类名)。
(2) 包装对象的构造过程,直接将产品返回给调用者,减轻调用者的负担。
(3) 至于用哪种工厂模式,就要依照具体项目的复杂程度了。简单的,不需要拓展的直接用简单工厂,单一的产品就用工厂模式,产品延伸链较长、类别较多则使用抽象工厂。