场景
工厂模式可以分为简单工厂、工厂、抽象工厂。
当用户需要一个类的子类实例,并且不希望与该类的子类形成耦合或者不知道该类有哪些子类可用时,可用采用工厂模式。
当用户需要系统提供多个对象,且希望和创建对象的类解耦时,可采用抽象工厂模式。
表1——小汽车工厂
单一产品系,工厂生产一种类的产品
高档 | 中档 | 低档 | |
---|---|---|---|
小汽车 | √ | √ | √ |
表2——小汽车、公交车工厂
多产品系,特征相同,工厂生产多种类型的产品。
高档 | 中档 | 低档 | |
---|---|---|---|
小汽车 | √ | √ | √ |
公交车 | √ | √ | √ |
表3——小汽车、公交车工厂
多产品系,部分特征相同。
高档 | 中档 | 低档 | |
---|---|---|---|
小汽车 | √ | √ | √ |
公交车 | √ | √ |
表4——小汽车、公交车工厂
多产品系,无特征相同。
高档 | 中档 | 低档 | |
---|---|---|---|
小汽车 | √ | √ | |
公交车 | √ |
分类
简单工厂
以简单工厂模式设计表1相关类。
public interface Car {
}
public class UpCar implements Car{
}
public class MidCar implements Car {
}
public class DnCar implements Car {
}
public class CarSimpleFactory {
public static final String UPTYPE = "uptype";
public static final String MIDTYPE = "midtype";
public static final String DNTYPE = "dntype";
public static Car create(String mark){
Car obj = null;
if (mark.equals(UPTYPE)){
obj = new UpCar();
}else if (mark.equals(MIDTYPE)){
obj = new MidCar();
}else if (mark.equals(DNTYPE)){
obj = new DnCar();
}
return obj;
}
public static void main(String[] args) {
Car obj = CarSimpleFactory.create(UPTYPE);
}
}
使用简单工厂的时候,通常不需要创建简单工厂类的类实例,因此简单工厂类可以看做一个工具类。简单工厂的方法通常是静态的,因此也被称作静态工厂。
如果要防止客户端无谓的创造简单工厂实例,还可以吧简单工厂的构造方法私有化。
如果现在又新增加一个超高档类型的汽车,在简单工厂模式下,需要做以下工作:
- 新增Car的子类SuperCar
- 修改工厂类SimpleCarFactory中的create()方法,增加SuperCar对象的判断选择分支
能否不修改工厂类,就完成所需的功能?
工厂
以工厂模式设计表1相关类。
public interface Car {}
public class UpCar implements Car {}
public class MidCar implements Car {}
public class DnCar implements Car {}
public abstract class AbstractFactory {
public abstract Car create();
}
public class UpFactory extends AbstractFactory {
@Override
public Car create() {
return new UpCar();
}
}
public class MidFactory extends AbstractFactory {
@Override
public Car create() {
return new MidCar();
}
}
public class DnFactory extends AbstractFactory {
@Override
public Car create() {
return new DnCar();
}
}
create(String mark)方法在简单工厂模式中,是成员方法,表明该方法管理多个产品,根据mark的值产生并返回Car对象。
create()方法在工厂模式中,是抽象方法,无参数,表明在具体的子类工厂中创建某个具体的产品。
工厂方法更易于软件二次开发及维护,当需求分析发生变化时,只需要增加、删除相应的类,而不是修改已有的类。如添加超高档汽车:
public class SuperCar implements Car {}
public class SuperFactory extends AbstractFactory {
@Override
public Car create() {
return new SuperCar();
}
}
工厂模式要优于简单工厂模式。
抽象工厂
一般来说,简单工厂、工厂模式是单产品系的,而抽象工厂是多产品系的。从本质上来说,抽象工厂、工厂模式是统一的。
产品
public interface Car {}
public class UpCar implements Car {}
public class MidCar implements Car {}
public class DnCar implements Car {}
public interface Bus {}
public class UpBus implements Bus {}
public class MidBus implements Bus {}
public class DnBus implements Bus {}
工厂类
public abstract class AbstractFactory {
public abstract Car createCar();
public abstract Bus createBus();
}
public class UpFactory extends AbstractFactory {
@Override
public Car createCar() {
return new UpCar();
}
@Override
public Bus createBus() {
return new UpBus();
}
}
public class MidFactory extends AbstractFactory {
@Override
public Car createCar() {
return new MidCar();
}
@Override
public Bus createBus() {
return new MidBus();
}
}
public class DnFactory extends AbstractFactory {
@Override
public Car createCar() {
return new DnCar();
}
@Override
public Bus createBus() {
return new DnBus();
}
}
应用示例
文件读取
编写读文件功能。具体功能:
- 读取文本文件:GBK、UTF8、UNCODE编码下的文本文件。
- 读取图像文件:BMP、GIF、JPG文件。要求获取图像宽度、长度、每一点的RGB三基色信息。
方法一
定义文件产品
定义文件产品
public interface Read {
void read(String fileName);
}
定义读文本、图像文件抽象类
public abstract class AbstractTextRead implements Read {
@Override
public abstract void read(String fileName);
}
public abstract class AbstractImgRead implements Read {
@Override
public abstract void read(String fileName);
}
定义具体读文本、图像文件类
public class GBKRead extends AbstractTextRead {
@Override
public void read(String fileName){
}
}
public class UTF8Read extends AbstractTextRead {
@Override
public void read(String fileName){
}
}
public class UNICODERead extends AbstractTextRead {
@Override
public void read(String fileName){
}
}
public class BMPRead extends AbstractImgRead {
@Override
public void read(String fileName) {
}
}
public class GIFRead extends AbstractImgRead {
@Override
public void read(String fileName) {
}
}
public class JPGRead extends AbstractImgRead {
@Override
public void read(String fileName) {
}
}
工厂模式
定义抽象工厂类
public abstract class AbstractFactory {
public abstract Read create();
}
定义具体工厂类
public class GBKFactory extends AbstractFactory {
@Override
public Read create() {
return new GBKRead();
}
}
public class UTF8Factory extends AbstractFactory {
@Override
public Read create() {
return new UTF8Read();
}
}
public class UNICODFactory extends AbstractFactory {
@Override
public Read create() {
return new UNICODERead();
}
}
public class BMPFactory extends AbstractFactory {
@Override
public Read create() {
return new BMPRead();
}
}
public class GIFFactory extends AbstractFactory {
@Override
public Read create() {
return new GIFRead();
}
}
public class JPGFactory extends AbstractFactory {
@Override
public Read create() {
return new JPGRead();
}
}
依据层次图,利用工厂模式可以直译除上述代码,但不是最优的层次架构。关键在于没有充分运用到JDK本身已有的类库,没有依据层次图做进一步的抽象。
方法二
JDK提供了不同编码下的字符串转换方法。其中一个重要的构造方法是:String(byte buf[], String encode)。因此读文本文件的思路是:按字节输入流把文件读入缓冲区buf,然后再按上述String构造方法,将buf缓冲区按encode编码方式进行编码,转换为可视字符串。
JDK提供了图像操作类ImageIO,封装了对各种图像文件的读写操作。
定义读接口
public interface Read<T> {
T read(String ... in);
}
定义读文件具体类
读文本文件
public class TextRead implements Read<String> {
@Override
public String read(String... in) {
String result = null;
try{
// in[0]表示文件名称
File file = new File(in[0]);
long len = file.length();
FileInputStream input = new FileInputStream(in[0]);
byte[] buf = new byte[(int) len];
input.read(buf);
// 按in[1]编码方式转化成可见字符串
result = new String(buf,in[1]);
input.close();
}catch (Exception e){
e.printStackTrace();
}
return result;
}
}
图像基本信息类
public class ImageInfo {
private int width; // 图像宽度
private int height; // 图像高度
private int r[][]; // 红色分量
private int g[][]; // 绿色分量
private int b[][]; // 蓝色分量
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int[][] getR() {
return r;
}
public int[][] getG() {
return g;
}
public int[][] getB() {
return b;
}
public void setRGB(int rgb[]){
r = new int[height][width];
g = new int[height][width];
b = new int[height][width];
int pos = 0;
for (int i = 0 ;i < height;i++){
pos = width * i;
for (int j = 0 ;j<width;j++){
r[i][j] = (rgb[pos+j]&0xff0000) >> 16;
g[i][j] = (rgb[pos+j]&0x00ff00) >> 8;
b[i][j] = rgb[pos+j]&0x0000ff;
}
}
}
}
读图像文件
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class ImageRead implements Read<ImageInfo> {
@Override
public ImageInfo read(String... in) {
String result = null;
ImageInfo obj = null;
try{
File f = new File(in[0]);
BufferedImage bi = ImageIO.read(f);
int width = bi.getWidth();
int height = bi.getHeight();
int rgb[] = new int[width*height];
bi.getRGB(0,0,width,height,rgb,width,height);
obj = new ImageInfo();
obj.setWidth(width);
obj.setHeight(height);
obj.setRGB(rgb);
}catch (Exception e){
e.printStackTrace();
}
return obj;
}
}
定义抽象工厂
public abstract class AbstractFactory {
public abstract Read create();
}
定义具体工厂
public class TextFactory extends AbstractFactory{
@Override
public Read create() {
return new TextRead();
}
}
public class ImageFactory extends AbstractFactory {
@Override
public Read create() {
return new ImageRead();
}
}
自动选择工厂
上述代码没有体现如何选择具体的工厂,以下给出几种解决方法。
方法一
在抽象工厂类中添加分支选择代码,根据mark标识产生不同的具体工厂类对象。
public abstract class AbstractFactory {
public static final String TEXT = "text";
public static final String IMAGE = "image";
public abstract Read create();
public static AbstractFactory create(String mark){
AbstractFactory factory = null;
if (mark.equals(TEXT)){
factory = new TextFactory();
}else if (mark.equals(IMAGE)){
factory = new ImageFactory();
}
return factory;
}
}
方法二
运用反射技术
public abstract class AbstractFactory {
public abstract Read create();
public static AbstractFactory create(String className){
AbstractFactory factory = null;
try{
Class c = Class.forName(className);
factory = (AbstractFactory) c.newInstance();
}catch (Exception e){
e.printStackTrace();
}
return factory;
}
}
反射方法的进一步修改
反射技术可以灵活地实现自动工厂选择功能
对于方法二中的抽象工厂模式,当添加新具体工厂类的时候,无须修改AbstractFactory类,因此该结构对抽象工厂来说是较为恰当的。
对于方法一中的工厂模式,由于每个工厂都对应单一产品,所以在运用反射技术的前提下没有必要利用反射技术先生成工厂,再生产具体产品。可直接利用反射技术产生具体的产品即可,该类也由抽象类变成普通类。代码如下:
public class ProductFactory<T> {
public T create(String className) {
T product = null;
try {
Class c = Class.forName(className);
product = (T) c.newInstance();
} catch (Exception e){
e.printStackTrace();
}
return product;
}
}
工厂与抽象工厂的对比
- 工厂模式:定义一个用于创建对象的借口,让子类决定实例化哪一个类
- 抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类