zoukankan      html  css  js  c++  java
  • Java设计模式:工厂模式

    问题提出

    Java的工厂模式与现实生活中的工厂的模型是很相似的。工厂是用来做什么?当然是用来生成产品。因此在Java的工厂模式的关键点就是如何描述好产品和工厂这2个角色之间的关系。

     

    下面来仔细描述一下不同的关系以及对应的设计模式

    1)单一产品系,工厂只生产一种类型的产品

    让我们简单构思一下简单的工厂生产模型,既然只有一个产品,那只需要一个车间就好了,一个车间分别生产三种不同种类的小汽车。好,下面看看简单工厂的设计模式。

    //定义小汽车接口:ICar.java
    public interface ICar {
    }
    public class TopCar implements ICar {
    }
    public class MidCar implements ICar {
    }
    public class LowCar implements ICar {
    }
    public class CarSimpleFactory {
        public static final String TOPTYPE = "toptype";
        public static final String MIDTYPE = "midtype";
        public static final String LOWTYPE = "lowtype";
    
        private CarSimpleFactory(){
            
        }
        public static ICar create(String mark){
            ICar obj = null;
            if(mark.equals(TOPTYPE)){
                obj = new TopCar();
            }
            else if(mark.equals(MIDTYPE)){
                obj = new MidCar();
            }
            else if(mark.equals(LOWTYPE)){
                obj = new LowCar();
            }
            return obj;
        }
    }

    简单工厂功能类编写步骤:

    1.编写抽象产品接口

    2.编写具体产品子类

    3.编写工厂类。简单工厂类的特点,它是一个具体的类,非接口型抽象类。有一个重要的create()方法,并且用if...else或者switch开发创建所需产品。因为可以把这个简单工厂类实现成一个工具类,所以直接用静态方法就可以了,如有必要甚至可以把构造方法私有化。

     

    下面考虑一种情形,工厂扩张啦!可以加工生产超级高档型的汽车。在上面的生产模式下要怎么修改呢?

    1)新增ICar子类SuperCar

    2)修改工厂类SimpleCarFactory中的create()方法,添加判断分支。

    子类SuperCar的添加是必然的,然而能不能不用修改工厂类呢?

    或者从另一个角度分析,在一个生产线上生产几种不同种类的产品,这样我们还需要对每个产品加以判断种类来分类。当然,假若只有2种种类要区分,这样当然没问题,但是若是有多个种类要区分呢?显然,不能在一条生产线上生产多种种类的产品!那意味着对于每一种种类我们都需要一条生产线。这就是所谓的工厂模式!

    public abstract class AbstractFactory {
        public abstract ICar create();
    }
    public class LowFactory extends AbstractFactory {
        public ICar create(){
            return new LowCar();
        }
    }
    View Code
    public class MidFactory extends AbstractFactory {
        public ICar create(){
            return new MidCar();
        }
    }
    View Code
    public class TopFactory extends AbstractFactory {
        public ICar create(){
            return new TopCar();
        }
    }
    View Code

    而观察代码,只要有ICar.java,AbstractFactory.java这两个文件,其他具体产品类,工厂类源文件没有,编译也能通过。

    对于AbstractFactory,可以定义为抽象类,也可以定义为接口。这里的create()是抽象方法,没参数,表明在具体的子类工厂中创建某个具体产品。

    工厂方法的主要特征是:当需求分析发生变化是,只需要增加和删除相应的类,而不是修改已有的类。例如添加超高档的小汽车,只需要增加SuperCar以及SuperFactory两个类即可。所以工厂方法更易于软件的二次开发以及维护。

    当然啦,一个工厂很有可能不止生成一种产品。下面看看多产品系。

    2)多产品系,特征相同

    UML图与工厂模式基本没什么区别,这里就不再画了。

    一般来说,简单工厂和工厂模式都是单产品系的,而对于上面这种架构,称之为抽象工厂。但从本质上来说,抽象工厂和工厂模式是统一的。这里就不再述说。

    3)多产品系,部分特征相同

    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(); //产生小汽车对象
    }

    这里着重看看AbstractFactoryAbstractFactory1AbstractFactory2.

    1)具有相同特征的小汽车和公共汽车放在相同的工厂里面

    2)该类也是抽象类,表明“特征是多个”。这里特征表示“高中低档”。

     

    工厂模式大概就讲到这里,看到这里,相信很多人都有一种,工厂模式原来也不过如此。但是,个人觉得无论什么设计模式,看上去都似乎很好理解很容易。但是,当遇到问题需要自己分析设计的时候,就顿时没了头绪。

    原因大概有2个,一是对这种设计模式还并不是十分熟悉和理解,二当然就是没有实践过。闲话不多说,下面让我们操刀实践一波。

     

     

    例子:编写读文件功能。读取文本文件,包括GBKUTF-8.编码下的文本文件,要求获得全文内容;读取图像文件,包括BMPGIFJPG文件,要求获得图像宽度,长度,每一点的三基色信息。

    代码如下:

    读取文本文件方法需要两个参数:文件名和编码方式;而读取图像文件需要一个参数:文件名。而根据题意,两种读取文件返回值类型有所差异。

    如何用接口屏蔽方法参数个数,返回值类型的差异,是定义接口的关键。

    public interface IRead<T> {
        //如何用接口屏蔽方法参数个数,返回值类型的差异,这是定义接口的关键
        T read(String ... in);
    }
    package factoryModel.explorer2;
    
    import java.io.File;
    import java.io.FileInputStream;
    
    /**
     * Created by lenovo on 2017/4/17.
     */
    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 len = file.length();
                FileInputStream input = new FileInputStream(in[0]);
                byte buf[] = new byte[(int)len];
                input.read(buf);
                result = new String(buf,in[1]);         //按照in[1]编码方式转化为可见字符串
                input.close();
            }
            catch (Exception ex){
                System.out.println(ex.getMessage());
            }
            return result;
        }
    }
    View Code
    package factoryModel.explorer2;
    
    /**
     * Created by lenovo on 2017/4/17.
     */
    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);
                }
            }
        }
    
    }
    View Code
    package factoryModel.explorer2;
    
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    /**
     * Created by lenovo on 2017/4/17.
     */
    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 (IOException ex){
                ex.printStackTrace();
            }
            int width = bi.getWidth();
            int height = bi.getHeight();
            int rgb[] = new int[width*height];
    
            bi.getRGB(0,0,width,height,rgb,width,height);       //将图像数据读到result缓存区
            ImageInfo imageInfo = new ImageInfo();
            imageInfo.setHeight(height);
            imageInfo.setWidth(width);
            return imageInfo;
        }
    }
    View Code
    public abstract class AbstractFactory {
      //定义抽象工厂
    public abstract IRead create(); }
    public class TextFactory extends AbstractFactory {
        public IRead create(){
            return new TextRead();
        }
    }
    public class ImageFactory extends AbstractFactory {
        public IRead create(){
            return new ImageRead();
        }
    }

    对于上面抽象工厂类其实并不够完善,如何选择具体工厂类没有体现。解决方法有两种。一是像简单工厂那样添加选择分支。二是使用反射。

    下面的代码是使用了反射技术

    public abstract class AbstractFactory {
        public abstract IRead create();
        static AbstractFactory create(String className){
            AbstractFactory factory = null;
            try{
                Class c = Class.forName(className);
                factory = (AbstractFactory)c.newInstance();
            }
            catch (Exception ex){
                ex.printStackTrace();
            }
            return factory;
        }
    }

    运用反射技术,实现了更加灵活的自动工厂选择功能。当添加新具体工厂类的时候,不需要修改AbstractFactory类。

  • 相关阅读:
    unrecognized selector sent to class
    Xcode 7安装KSImageNamed 不启作用
    使用IntelliJ IDEA配置Erlang开发环境
    BN 详解和使用Tensorflow实现(参数理解)
    argparse 在深度学习中的应用
    转置卷积的详细理解
    递归该怎么写(二)
    递归该怎么写(一)
    二叉树的遍历python 代码
    经典排序的python实现
  • 原文地址:https://www.cnblogs.com/lateink/p/6725599.html
Copyright © 2011-2022 走看看