设计模式-工厂方法模式

171 阅读4分钟

概念

定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行.工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)

应用场景

由于简单工厂只提供一个工厂类来进行对象的创建,导致其职责过重,且代码不易维护,当系统需要扩展时,需要修改原有代码,导致违反了开闭原则,故引出工厂方法模式。如下情况可以考虑使用工厂方法模式

  • 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体类名,只需要知道对应的工厂即可,具体的产品由具体工厂对象进行创建,可以将具体工厂类的类名存储在配置文件或者数据中,进行动态创建

具体应用

工厂方法模式就是利用了面向对象的多态性,提供抽象工厂类型,让其实现类(具体工厂类)来决定要创建的具体对象

该模式主要包含如下角色:

  • Factory(抽象工厂):在抽象工厂类中,声明了一个创建方法,用于返回一个抽象产品
  • ConcreteProduct(具体工厂):是抽象工厂的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品实例
  • Product(抽象产品):定义产品的接口,是工厂方法所创建对象的公共父类
  • ConcreteProduct(具体产品):实现了抽象产品接口,某种类型由专门的具体工厂进行创建,具体工厂和具体产品之间一一对应

代码实践

还是绘制图形,我们使用工厂方法模式来进行绘制图形,并且增加了一个使用案例,即通过读取配置文件+反射的机制来实现运行时动态生成不同的对象。 首先定义一个产品抽象类,这里我们的产品就是图形Graphical

/**
 * 抽象产品类
 * circular 圆形
 * triangle 三角形
 */
public abstract class Graphical {
    public abstract void draw();
    public abstract void erase();
}

定义我们的具体产品,这里具体的产品为三角形,和圆形。不支持方形

public class Circular extends Graphical {
    @Override
    public void draw() {
        System.out.println("画了一个圆形");
    }

    @Override
    public void erase() {
        System.out.println("擦掉了一个圆形");
    }
}
public class Triangle extends Graphical {
    @Override
    public void draw() {
        System.out.println("画了一个三角形");
    }

    @Override
    public void erase() {
        System.out.println("擦掉了一个三角形");
    }
}

然后定义抽象工厂类GraphicalFactory

public interface GraphicalFactory {
    public Graphical createGraphical();
}

定义具体工厂类,因为我们有2个具体产品(三角形和圆形),所以有2个具体工厂(CircularGraphicalFactory和TriangleGraphicalFactory)

//圆形工厂
public class CircularGraphicalFactory implements GraphicalFactory {
    @Override
    public Graphical createGraphical() {
        return new Circular();
    }
}

//三角形工厂 
public class TriangleGraphicalFactory implements GraphicalFactory {
    @Override
    public Graphical createGraphical() {
        return new Triangle();
    }
}

定义通过配置文件创建对象的工具类ConfigUtil

public class ConfigUtil {

    public static Object getBean() {
        try {
            InputStream in = Test.class.getClassLoader().getResourceAsStream("Graphical.properties");
            Properties props = new Properties();
            InputStreamReader inputStreamReader = new InputStreamReader(in, "UTF-8");
            props.load(inputStreamReader);

            String cName=props.getProperty("factoryClassName");
            //通过类名生成实例对象并将其返回
            Class c=Class.forName(cName);
            Object obj=c.newInstance();
            return obj;
        }
        catch(Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

配置文件 Graphical.properties

factoryClassName=com.donkeyx.pattern.factory.factorymethod.TriangleGraphicalFactory

测试类

public class Test {

    public static void main(String[] args) {
        System.out.println("---------------------------------使用默认方式进行创建----------------------------------");
        defaultMethod();
        System.out.println("---------------------------------读取配置文件方式进行动态创建----------------------------------");
        readConfigFile();
    }


    /**
     * 使用默认方式进行创建
     */
    public static void defaultMethod(){
        //创建一个圆形
        GraphicalFactory factory;
        factory = new CircularGraphicalFactory();
        Graphical circular = factory.createGraphical();
        circular.draw();
        circular.erase();
        //创建一个三角形
        factory = new TriangleGraphicalFactory();
        Graphical triangle = factory.createGraphical();
        triangle.draw();
        triangle.erase();
    }


    /**
     * 读取配置文件方式进行动态创建
     */
    public static void readConfigFile() {
        GraphicalFactory factory;
        factory = (GraphicalFactory)ConfigUtil.getBean();
        Graphical graphical = factory.createGraphical();
        graphical.draw();
        graphical.erase();
    }
}

结果 在这里插入图片描述

优点

  • 在新产品加入时,无须修改抽象工厂和抽象产品的接口,无须修改客户端,也无需修改其他的具体工厂和具体产品,只需要再加入一个具体工厂和具体产品就可以了,符合开闭原则,提高了系统的可扩展性

缺点

  • 在新产品加入时,需要再加入一个具体工厂和具体产品,类的个数容易过多,增加了代码结构的复杂度
  • 增加了系统的抽象性和理解难度