Android设计模式之工厂模式 Factory

一.概述

平时做项目跟使用第三方类库的时候经常会用到工厂模式.什么是工厂模式,简单来说就是他的字面意思.给外部批量提供相同或者不同的产品,而外部不需要关心工厂是如何创建一个复杂产品的过程.所以工厂模式可以降低模块间的耦合,同时可以提高扩展性(当有新的产品出现时,只需要扩展工厂就行了,上层模块不敏感).

   工厂模式根据抽象的角度和层级的不同可以分为两种模式:

   1.工厂方法模式 (Factory Method)

   2.抽象工厂模式 (Abstract Factory)

二.实现

1.工厂方法模式

工厂方法模式的特点是:

   一个抽象产品类(或接口),派生(或实现)多个真实产品类

   一个抽象工厂类(或接口),派生(或实现)多个真实工厂类

   一般来说标准的工厂方法模式需要一个工厂只生产一种产品,那么当产品种类特别多的时候工厂的数量就特别多,所以通常会使用一些工厂方法模式的变种.

1.)标准工厂方法模式

   首先先介绍一下标准的工厂方法模式,不带任何的变种.以工厂生产不同操作系统的手机为例.

   建立一个产品接口,提供一个获取系统信息的方法.

public interface IPhone {
    public void getOS();
}

再根据IPhone接口实现Android,IOS,Blackberry三种手机.


public class AndroidPhone implements IPhone {
    private final String TAG = AndroidPhone.class.getSimpleName();
    @Override
    public void getOS() {
        Log.i(TAG, "im Android");
    }
}
public class IosPhone implements IPhone {
    private final String TAG = IosPhone.class.getSimpleName();
    @Override
    public void getOS() {
        Log.i(TAG, "im IOS");
    }
}
public class BlackBerryPhone implements IPhone {
    private final String TAG = BlackBerryPhone.class.getSimpleName();
    @Override
    public void getOS() {
        Log.i(TAG, "im BlackBerry");
    }
}

标准的工厂方法模式都需要有抽象的工厂接口或者基类.

public abstract class IGenerator {  
    public abstract IPhone generatePhone(String flag) throws Exception;  
} 

通过基类或者接口来实现真实的工厂类,这里需要注意跟简单工厂模式的不同,标准的工厂方法模式里面一个工厂只生产一个产品,所以这里要根据产品的种类划分出来三个工厂,分别生产三种不同的产品.这种设计思想非常契合单一职责原则.

public class AndroidGenerator extends IGenerator {
    @Override
    public IPhone generatePhone() {
        return new AndroidPhone();
    }
}
public class IOSGenerator extends IGenerator {
    @Override
    public IPhone generatePhone() {
        return new IosPhone();
    }
}
public class BlackberryGenerator extends IGenerator {
    @Override
    public IPhone generatePhone() {
        return new BlackBerryPhone();
    }
}

在客户端从工厂中获得产品并使用的过程中都是通过接口进行访问的,在创建产品的阶段有效得降低了使用者和产品之间的耦合度.

IPhone android, ios, bb;
IGenerator androidGenerator, iosGenerator, bbGenerator;
androidGenerator = new AndroidGenerator();
iosGenerator = new IOSGenerator();
bbGenerator = new BlackberryGenerator();
android = androidGenerator.generatePhone();
ios = iosGenerator.generatePhone();
bb = bbGenerator.generatePhone();
android.getOS();
ios.getOS();
bb.getOS();

最终的运行效果显而易见.

2).简单工厂模式

接着分析一下简单工厂模式,这是最简单的变种,也叫做静态工厂方法模式,从这个名字就可以看出工厂的方法是静态的.既然工厂方法是静态的,那么工厂就不能通过继承进行扩展,如果有新增的产品,就只能在静态方法里面做修改所以从这个角度来说简单工厂模式是不符合开闭原则的.

因为这是静态工厂方法模式,所以工厂类就没有接口或者虚基类来提供抽象.通过不同的Flag来初始化不同的产品.

public class PhoneGenerator{
    public final static String GENERATE_IOS = "generate_ios";
    public final static String GENERATE_ANDROID = "generate_android";
    public final static String GENERATE_BLACKBERRY = "generate_blackberry";

    public static IPhone generatePhone(String flag) throws Exception {
        IPhone iPhone = null;
        switch (flag){
            case GENERATE_ANDROID:
                iPhone =  new AndroidPhone();
                break;
            case GENERATE_IOS:
                iPhone =  new IosPhone();
                break;
            case GENERATE_BLACKBERRY:
                iPhone =  new BlackBerryPhone();
                break;
            default:
                throw new Exception("UNDEFINED FLAG");
        }
        return iPhone;
    }
}

对外部来说要使用工厂只需要把目标产品类传过去就行了.运行结果跟1)中的是一样的.

IPhone android, ios, bb;
android=PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_ANDROID);
ios=PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_IOS);
bb=PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_BLACKBERRY);
android.getOS();
ios.getOS();
bb.getOS();

3)结合反射的应用

假设需要加入一种搭载win10系统的手机,标准的工厂方法模式需要重新派生出来一个新的工厂来给客户使用,简单工厂模式也需要新增新的flag和case判断去构造新的手机.有没有什么方法可以尽量避免这些修改呢?当然是有的,这里可以通过使用Class.forName 反射的方式来达到目的.

首先通过泛型来约束输入输出的参数类型,把异常抛到上层去处理并实现具体的工厂.

public abstract class IGenerator {  
    public abstract <T extends IPhone>T generatePhone(Class<T> clazz) throws Exception;  
} 
public class PhoneGenerator extends IGenerator {
    public <T extends IPhone>T generatePhone(Class<T> clazz) throws Exception {
        IPhone iPhone = null;
        iPhone = (IPhone) Class.forName(clazz.getName()).newInstance();
        return (T)iPhone;
    }
}

通过这种装载的方式去初始化产品就可以达到上面描述的需求,可以根据需求直接添加一个实现了IPhone接口的WindowsPhone产品而不需要修改工厂,客户就可以直接从工厂拿到WindowsPhone的手机去使用了.

4)产品类私有构造应用

产品类私有构造应用其实更偏向与一种规范.既然使用工厂模式了,那就是这些手机全部都要在工厂内部创建出来.这种应用就做了限制,使用私有构造就不允许外部通过new的方式来创建,而工厂则通过反射和更改访问权限来创建产品.当然这个时候外部也可以通过同样的方式来创建对象,所以说这个应用更偏向于一种团队规范.

public class PhoneGenerator extends IGenerator {
    public <T extends IPhone>T generatePhone(Class<T> clazz) throws Exception {
        IPhone iPhone = null;
        Class phone = Class.forName(clazz.getName());
        phone.getDeclaredConstructor().setAccessible(true);
        iPhone = (IPhone) phone.newInstance();
        return (T)iPhone;
    }
}

5)缓存对象

对于那些创建起来特别消耗资源或者特别复杂的对象,可以使用下面的方式来进行一个长期的缓存.对于那些有访问数量需求的对象也可以建立缓存List,通过设置最大创建数来控制对象量级的峰值.例如JDBC的最大连接数等.

public class PhoneGenerator extends IGenerator{
    private Map<String, IPhone> map = new HashMap<>();
    @Override
    public <T extends IPhone> T generatePhone(Class<T> clazz) throws Exception{
        IPhone iPhone = null;
        if (map.containsKey(clazz.getName()))
            iPhone = map.get(clazz.getName());
        else {
            iPhone = (IPhone) Class.forName(clazz.getName()).newInstance();
            map.put(clazz.getName(), iPhone);
        }
        return (T) iPhone;
    }
}

2.抽象工厂模式

抽象工厂模式的特点:

   多个抽象产品类(或接口),派生(或实现)多个真实产品类

   一个抽象工厂类(或接口),派生(或实现)多个真实工厂类

   抽象工厂模式其实也算是工厂方法模式的一种延伸,在工厂方法模式中所有的产品都是一个系列的,都是从IPhone那里实现出的不同真实产品,所以对于外部来说他们都是手机产品,只需要关心手机的抽象接口就行了.然而又多个业务种类,并且这些业务有些依赖关系的时候,这种情况下使用的工厂模式就是抽象工厂模式.

   接着在抽象方法模式里面的例子,在抽象工厂模式中我们需要再多开一种产品,那就平板吧,而平板又分为Android平板和IOS平板(这里偷懒黑莓平板就不写了),并且平板跟手机有很多共同的地方,例如相同的OS硬件设计等.既然是一个新的产品线了,那么还是先抽象出来平板的接口来.还是老样子,打印一下自己是什么系统的.
public interface IPad {
    public void getBrand();
}

接着通过IPad接口来实现两个不同的平板.

  public class AndroidPad implements IPad {
    private final String TAG=AndroidPad.class.getSimpleName();
    @Override
    public void getBrand() {
        Log.i(TAG, "im Android pad");
    }
}
public class IOSPad implements IPad {
    private final String TAG = IOSPad.class.getSimpleName();
    @Override
    public void getBrand() {
        Log.i(TAG, "im IOS phone pad");
    }
}

在抽象工厂的接口的时候还是继续使用泛型来创建了,这样也省的派生出来几个不同的工厂.

public abstract class IGenerator {
    public abstract <T extends IPhone>T generatePhone(Class<T> clazz) throws Exception;
    public abstract <T extends IPad>T generatePad(Class<T> clazz) throws Exception;
}
public class ProductGenerator extends IGenerator{
    @Override
    public <T extends IPhone> T generatePhone(Class<T> clazz) throws Exception{
        IPhone iPhone = (IPhone) Class.forName(clazz.getName()).newInstance();
        return (T) iPhone;
    }

    @Override
    public <T extends IPad> T generatePad(Class<T> clazz) throws Exception {
        IPad iPad = (IPad) Class.forName(clazz.getName()).newInstance();
        return (T) iPad;
    }
}

假设有一个客户需要来我厂定制移动产品,A套餐中包含一个IOS手机和一个Android的平板,B套餐中包含一个Android手机和一个IOS平板.而这个套餐规则可以通过工厂进行约束.这样工厂就能胜任完成这个需求的任务了.

然而恰恰因为抽象工厂模式支持多种产品线,结果导致需要扩展一条新的产品的时候就会比较麻烦.假设需要新增一个屏幕贴膜产品,并且给每个出厂的带屏幕的产品都配一个.那么要做的修改不仅仅是要添加贴膜这个产品,还要修改从工厂的抽象到工厂的实现,还要修改工厂的约束.这是不符合开闭原则的.但是如果只是扩展一个产品的子系列,例如要新增一个windows平板,抽象工厂模式和工厂方法模式一样根本不需要修改工厂抽象和工厂实现,只需要新增产品就行了.

相关内容推荐