工厂方法模式一种类模式,也是创建型模式。它是最常用的设计模式。相关代码已同步github

在了解工厂方法模式之前,我们先看下工厂方法模式

场景

现需要设计一个程序(Client)来读取多种不同类型的图片格式,针对每一种图片格式都设计一个图片读取(ImageReader),如GIF图片读取器(GifReader)用于读取GIF格式的图片、JPG图片读取器(JpgReader)用于读取JPG格式的图片。

(括号中为工厂方法模式的提示:图片读取器对象通过图片读取器工厂ImageReaderFactory来创建,ImageReaderFactory是一个抽象类,用于定义创建图片读取器的工厂方法,其子类GifReaderFactory和JpgReaderFactory用于创建具体的图片读取器对象)

简单工厂模式

所谓工厂,就是一个加工的场所,为了修改时不同动用原始代码,即更好地满足“开闭原则”。

其实如果初学者在看资料时会很疑惑,为什么23中设计模式中没有简单工厂模式,但是它却十分常见的?

  • 因为简单工厂模式和工厂方法模式其实是同气连枝的,当工厂方法模式只有一个工厂时,就会抽象为简单工厂模式
  • 如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”

一共包含四个角色:

  • 工厂:加工使用产品
  • 产品接口
  • 具体产品实现类
  • 客户端
类图

在这里插入图片描述

实例
  • 当我们不采用任何设计模式完成上述场景问题时:

    // 当增加新功能的时候,需要直接修改产品
    public class ImageReader{
        public static void imageReader(String type){
            if(type.equals("GIF")){
                System.out.println("GIF");
            } else if(type.equals("Jpg")){
                System.out.println("Jpg");
            }
        }
    }
    public class Client{
        public static void main(String[] args){
            ImageReader.imageReader("GIF"); // "Jpg"
        }
    }
    

我们可以发现,这严重的违反了开闭原则,当我们需要对程序进行扩展的时候,需要我们修改ImageReader,即源文件!

  • 当我们采用简单工厂模式的时候:

    产品

    当增加新功能的时候,不用对产品进行修改,只用扩展就行,符合开闭原则

    interface ImageReader{
        void imageReader();
    }
    public class GifReader{
        @Override
        public void imageReader(){
            System.out.println("GIF");
        }
    }
    public class JpgReader{
        @Override
        public void imageReader(){
            System.out.println("JPG");
        }
    }
    

    工厂

    如果增加新功能的话还是需要修改此处

    public class ImageReaderFactory{
     	public static void getImageReader(String type){
            if(type.equals("GIF")){
                return new GifReader();
            } else if(type.equals("Jpg")){
                return new JpgReader();
            }
        }
    }
    

    客户端

    public class Client{
        public static void main(String[] args){
            // 此处也可以把gif写到xml中
            ImageReaderFactory.getImageReader("GIF").imageReader(); // "Jpg"
        }
    }
    

其实,Java源码中的java.text.DateFormate某种程度上也起到了工厂的功能,从它的一些方法中:

public static final DateFormat getDateInstance();
public static final DateFormat getDateInstance(int style);
public static final DateFormat getDateInstance(int style, Locale aLocale);

client会调用这些方法获取实例,这些方法就可以看成一个“简单工厂”

利弊

从上面的例子我们可以看出:

  • 工厂类含有必要的判断逻辑,即使配置XML也需要
  • 通过产品和工厂实现了对责任的分割,符合单一职责原则
  • 工厂类中集中了所有产品的创建逻辑,工厂类不够灵活,一旦工厂出错,整个程序将瘫痪

工厂方法

  • 工厂方法是为了解决简单工厂模式而诞生的
  • 更好地利用了面向对象的多态,将工厂抽象,使得不用修改工厂就可以轻松扩展程序
  • 比简单工厂模式更符合开闭原则

一共包含五个角色:

  1. 产品接口
  2. 实际产品
  3. 工厂接口
  4. 实际生成产品的工厂
  5. 客户端
类图

实例

我们根据博客开篇的场景说一下工厂方法模型:

产品

当新增产品需求时,产品代码不用改变,只需新增加一个产品类实现产品接口即可

public interface ImageReader {
    void imageReader();
}

public class JpgReader implements ImageReader{

    @Override
    public void imageReader() {
        System.out.println("Jpg reader is init");
    }
}

public class GifReader implements ImageReader {

    @Override
    public void imageReader() {
        System.out.println("Gif reader is init");
    }
}

工厂

当新增产品需求时,产品代码不用改变,只需新增加一个工厂类即可

public interface ImageReaderFactory {
    ImageReader getImageReader();
}

public class GifReaderFactory implements ImageReaderFactory{

    @Override
    public ImageReader getImageReader() {
        return new GifReader();
    }
}

public class JpgReaderFactory implements ImageReaderFactory{

    @Override
    public ImageReader getImageReader() {
        return new JpgReader();
    }
}

客户端

public class Client {

    private final static String FACTORY_METHOD = "factoryMethod";
    public static void runFactory() {

        String packageName = ImageReaderFactory.class.getPackageName();
        // 下面是一个XML文件读取的方法,我Github中有写,这里不赘述。
        // 当新增产品时,只需增加相应的实现类,修改XML中的配置文件即可,完全不用修改源代码
        ImageReaderFactory imageReaderFactory = (ImageReaderFactory)ReadXml.getObject(packageName, FACTORY_METHOD);

        if (imageReaderFactory == null){
            System.out.println("xml 配置错误");
        } else {
            imageReaderFactory.getImageReader().imageReader();
        }

    }
}

应用

  • Spring IOC 控制反转即用的工厂模式

  • Mybatis 中的 SqlSessionFactory

  • java.util.Collection.iterator()

在这里插入图片描述对于iterator来说:

Collection.iterator即是一个抽象工厂方法,而List.iterator等子类的该方法则是具体实现方法

而抽象的产品则是Iterator这个接口,具体的产品则是List等子类中的内部类Itr,查看源码可知,Itr实现了Iterator这个接口。

同时,具体工厂也调用了Itr这个内部产品类

总结

  • 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名
  • 工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。基于工厂角色和产品角色的多态性设计是工厂方法模式的关键
  • 完美符合开闭原则
  • 工厂方法可以退化为简单工厂模式
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐