一般来说,简单工厂、工厂模式是单产品系的,抽象工厂是多产品系的。从本质上来说,抽象工厂、工厂模式是统一的。
public interface ICar {
// 由于工厂模式仅关系对象的创建,为说明方便,无需定义方法
}
public interface IBus {
}
public class TopCar implements ICar {}
public class MidCar implements ICar {}
public class LowCar implements ICar {}
public class UpBus implements IBus {}
public class MidBus implements IBus {}
public class DnBus implements IBus {}
public abstract class AbstractFactory {
public abstract ICar createCar();
public abstract IBus createBus();
}
public class TopFactory extends AbstractFactory {
@Override
public ICar createCar() {
return new TopCar();
}
@Override
public IBus createBus() {
return new UpBus();
}
}
public class MidFactory extends AbstractFactory {
@Override
public ICar createCar() {
return new MidCar();
}
@Override
public IBus createBus() {
return new MidBus();
}
}
public class LowFactory extends AbstractFactory {
@Override
public ICar createCar() {
return new LowCar();
}
@Override
public IBus createBus() {
return new DnBus();
}
}
public class FactoryTest {
public static void main(String[] args) {
AbstractFactory obj = new TopFactory();
ICar car = obj.createCar();
}
}
代码分析:
1)抽象工厂模式功能类编制步骤:
- 定制抽象产品呢接口,如ICar,IBus。
- 定制具体产品子类。如小汽车类TopCar、MidCar、LowCar,公共汽车类UpBus、MidBus、DnBus。
- 定制抽象工厂类(或接口),如AbstractFactory。其中有两个重要的create( )抽象方法,分别返回ICar、IBus对象。
- 定制具体工厂子类,如TopFactory、MidFactory、LowFactory,每个工厂重写create( )方法。
2)从本质上来说,抽象工厂与工厂模式是统一的,只不过抽象工厂是多产品系的,工厂模式是单产品系。
典型模型语义分析
抽象工厂的语义描述:设A产品有A1,A2,..., AN,B产品B1, B2, ..., Bn,共享特征C,C有C1, C2, ..., Cn 。即C1特征产品A1、B1,C2的特征产品A2, B2, ..., Cn的特征产品An,Bn。把该语义翻译成计算机程序。
其他情况:
多产品系,局部特征情况相同,小汽车和公共汽车都有高、中档类型,小汽车有抵挡类型,公共汽车没有。
public abstract class AbstractFactory { }
public abstract class AbstractFactory1 extends AbstractFactory{
public abstract ICar createCar();
public abstract IBus createBus();
}
public abstract class AbstractFactory2 extends AbstractFactory {
public abstract ICar createCar();
}
public class TopFactory extends AbstractFactory1 {
@Override
public ICar createCar() {
return new TopCar();
}
@Override
public IBus createBus() {
return new UpBus();
}
}
public class MidFactory extends AbstractFactory1 {
@Override
public ICar createCar() {
return new MidCar();
}
@Override
public IBus createBus() {
return new MidBus();
}
}
public class LowFactory extends AbstractFactory2 {
@Override
public ICar createCar() {
return new LowCar();
}
}
多产品系,无特征情况相同,小汽车有高、中档类型,小汽车无低档类型,公共汽车有低档类型。
应用探究:
编写文件功能。具体功能是:读取文本文件,包括(GBK,UTF8,UNICODE)编码下的文本文件,要求获得全文内容;读取图像文件(BMP,GIF,JPG)文件,要求获得图像宽度、长度、每一点的RGB三基色信息。
方法一:根据语义,分别画出功能层次图。
1)定义文件产品
2)定义工厂类(利用工厂模式)
利用工厂模式可以直译上述代码。但是,这种层次框架并不是最优的,关键在于没有充分的利用到JDK本身已有类库,没有在此基础上进一步抽象。
方法2:
分析:1. JDK中提供了不同编码下的字符串转换方法,其中一个重要的构造方法是String(byte buf[], String encode)。因此得出读文本文件的思路,即按字节输入流把文件读入缓冲区buf,再按上述String构造方法,将buf缓冲区按encode编码方式进行转码,转化成可视字符串。将这种方法不但适用于GBK、UTF8、UNICODE编码的文本文件,还适用于其他编码文件。
2. JDK中提供了图像操作类,如ImageIO,封装了对BMP、GIF、JPG等格式图像文件的读写操作。利用ImageIO,可大大减少代码编码量,提高编程效率。
1)定义读(文本、图像)文件接口
该文本文件方法需要两个参数:文件名,文件编码方式;读图像文件需要一个参数:文件名。根据题意,读文本文件要求返回String类型,读图像文件要求返回图像长、宽、RGB复合信息。如何用接口屏蔽方法参数个数,返回值类型的差异,是定义接口的关键。
定义泛型接口是解决返回值类型不同的较好方法,而屏蔽方法参数个数差异利用“String ... in” 形式即可实现。根据接口,利用工厂模式,UML图:
2)定义读文本文件、图像文件具体类
public class TextRead implements IRead<String> {
// 读取文本
public String read(String ... in){ // 可输入0或多个参数
String result = null; //result是结果串
try {
File file = new File(in[0]); // in[0] 表示文件名称
long length = file.length();
FileInputStream input = new FileInputStream(in[0]);
byte buf[] = new byte[(int) length]; // 缓冲区大小等于文件长度
input.read(buf); // 一次读完文件
result = new String(buf, in[1]); // 按in[1]编码方式转化成可见字符串
input.close();
}catch (Exception e){
System.out.println(e.getMessage());
}
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 void setR(int[][] r) {
this.r = r;
}
public int[][] getG() {
return g;
}
public void setG(int[][] g) {
this.g = g;
}
public int[][] getB() {
return b;
}
public void setB(int[][] b) {
this.b = 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;
}
}
}
}
public class ImageRead implements IRead<ImageInfo> {
public ImageInfo read(String ... in){
BufferedImage bi = null;
File f = new File(in[0]);
try {
bi = ImageIO.read(f);
} catch (Exception e){
e.printStackTrace();
}
int width = bi.getWidth();
int height = bi.getHeight();
int[] rgb = new int[width * height];
// 将图像数据读到result 缓冲区
bi.getRGB(0,0, width, height, rgb, width, height);
ImageInfo obj = new ImageInfo();
obj.setWidth(width);
obj.setHeight(height);
obj.setRGB(rgb);
return obj;
}
}
- 自定义ImageInfo 类与JDK 系统类ImageIO完成了对图像文件的读操作。ImageIO中的read()方法可把某图像矩形区域像素点的三基色值统一读到一维整形数组中,因此必须对该数组每一值进行移位拆分,获得分别r、g、b值。拆分过程如ImageInfo中的setRGB()方法。
- 利用IMageIO类,简化了读不同格式图像文件基础类的编制,仅有一个类就可以了。ImageIO类不但有读功能,还有写功能,都支持那学图像文件呢?
3) 定义抽象工厂
4) 定义具体工厂
自动选择工厂
上面的代码中给出了工厂类的功能代码,如何选择具体工厂没有体现。其实,与简单工厂类中选择分支一样,在抽象类中加相应的代码就可以了。
抽象方法 create( ) 语义是:具体工厂类对象是由客户端调用方产生的;静态方法create( )语义是:具体工厂类对象是在本类产生的,根据mark标识自动产生不同的具体工厂类对象。本类暗含了两种产生工厂对象的方法,方便用户加以选择。
运用反射技术,实现了更加灵活的自动工厂选择功能。当增加新具体工厂类时,无需修改AbstractFactory类。仔细分析,该类结构对抽象工厂模式是最恰当的。抽象工厂对应多产品簇,每个具体共产包含多种产品。从层次清晰角度来说,也应该先得到具体工厂,在得到该工厂中的某个具体产品。但对于简单工厂、工厂模式而言,他们都对应单一产品簇。在运用反射技术的前提下,没有必要利用发射先生产具体工厂,在产生具体产品,直接反射产生具体产品就可以了。而且该类也可由抽象类变为普通类。
分析代码得出,产品工厂类ProductFactory 适用于返回IRead产品的。稍加改造,运用泛型技术,可以得出更一般的形式。