09 【创建型模式】工厂三兄弟之简单工厂模式

2017-12-23  本文已影响10人  猿笔记

定义

  简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式

要点

  当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。

结构图

要素

abstract class Product {  
    //所有产品类的公共业务方法  
    public void methodSame() {  
        //公共方法的实现  
    }  
  
    //声明抽象业务方法  
    public abstract void methodDiff();  
}  
class ConcreteProduct extends Product {  
    //实现业务方法  
    public void methodDiff() {  
        //业务方法的实现  
    }  
}  
class Factory {  
    //静态工厂方法  
    public static Product getProduct(String arg) {  
        Product product = null;  
        if (arg.equalsIgnoreCase("A")) {  
            product = new ConcreteProductA();  
            //初始化设置product  
        }  
        else if (arg.equalsIgnoreCase("B")) {  
            product = new ConcreteProductB();  
            //初始化设置product  
        }  
        return product;  
    }  
}  
class Client {  
    public static void main(String args[]) {  
        Product product;   
        product = Factory.getProduct("A"); //通过工厂类创建产品对象  
        product.methodSame();  
        product.methodDiff();  
    }  
}  

示例

  Sunny软件公司欲基于Java语言开发一套图表库,该图表库可以为应用系统提供各种不同外观的图表,例如柱状图、饼状图、折线图等。Sunny软件公司图表库设计人员希望为应用系统开发人员提供一套灵活易用的图表库,而且可以较为方便地对图表库进行扩展,以便能够在将来增加一些新类型的图表。

原始设计

class Chart {  
    private String type; //图表类型  
      
    public Chart(Object[][] data, String type) {  
        this.type = type;  
        if (type.equalsIgnoreCase("histogram")) {  
            //初始化柱状图  
        }  
        else if (type.equalsIgnoreCase("pie")) {  
            //初始化饼状图  
        }  
        else if (type.equalsIgnoreCase("line")) {  
            //初始化折线图  
        }  
    }  
  
    public void display() {  
        if (this.type.equalsIgnoreCase("histogram")) {  
            //显示柱状图  
        }  
        else if (this.type.equalsIgnoreCase("pie")) {  
            //显示饼状图  
        }  
        else if (this.type.equalsIgnoreCase("line")) {  
            //显示折线图  
        }     
    }  
}  

设计中存在如下几个问题:

   (1) 在Chart类中包含很多“if…else…”代码块,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断。

   (2) Chart类的职责过重,它负责初始化和显示所有的图表对象,将各种图表对象的初始化代码和显示代码集中在一个类中实现,违反了“单一职责原则”,不利于类的重用和维护;而且将大量的对象初始化代码都写在构造函数中将导致构造函数非常庞大,对象在创建时需要进行条件判断,降低了对象创建的效率。

   (3) 当需要增加新类型的图表时,必须修改Chart类的源代码,违反了“开闭原则”。

   (4) 客户端只能通过new关键字来直接创建Chart对象,Chart类与客户端类耦合度较高,对象的创建和使用无法分离。

   (5) 客户端在创建Chart对象之前可能还需要进行大量初始化设置,例如设置柱状图的颜色、高度等,如果在Chart类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设置,这些代码在每次创建Chart对象时都会出现,导致代码的重复。

解决方案

//抽象图表接口:抽象产品类  
interface Chart {  
    public void display();  
}  
  
//柱状图类:具体产品类  
class HistogramChart implements Chart {  
    public HistogramChart() {  
        System.out.println("创建柱状图!");  
    }  
      
    public void display() {  
        System.out.println("显示柱状图!");  
    }  
}  
  
//饼状图类:具体产品类  
class PieChart implements Chart {  
    public PieChart() {  
        System.out.println("创建饼状图!");  
    }  
      
    public void display() {  
        System.out.println("显示饼状图!");  
    }  
}  
  
//折线图类:具体产品类  
class LineChart implements Chart {  
    public LineChart() {  
        System.out.println("创建折线图!");  
    }  
      
    public void display() {  
        System.out.println("显示折线图!");  
    }  
}  
  
//图表工厂类:工厂类  
class ChartFactory {  
    //静态工厂方法  
    public static Chart getChart(String type) {  
        Chart chart = null;  
        if (type.equalsIgnoreCase("histogram")) {  
            chart = new HistogramChart();  
            System.out.println("初始化设置柱状图!");  
        }  
        else if (type.equalsIgnoreCase("pie")) {  
            chart = new PieChart();  
            System.out.println("初始化设置饼状图!");  
        }  
        else if (type.equalsIgnoreCase("line")) {  
            chart = new LineChart();  
            System.out.println("初始化设置折线图!");              
        }  
        return chart;  
    }  
}  

class Client {  
    public static void main(String args[]) {  
        Chart chart;  
        chart = ChartFactory.getChart("histogram"); //通过静态工厂方法创建产品  
        chart.display();  
    }  
}  

仍存在的问题

  发现在创建具体Chart对象时,每更换一个Chart对象都需要修改客户端代码中静态工厂方法的参数,客户端代码将要重新编译,这对于客户端而言,违反了“开闭原则”。

进一步改进方案

  将静态工厂方法的参数存储在XML或properties格式的配置文件中

<?xml version="1.0"?>  
<config>  
    <chartType>histogram</chartType>  
</config>  

import javax.xml.parsers.*;  
import org.w3c.dom.*;  
import org.xml.sax.SAXException;  
import java.io.*;  
  
public class XMLUtil {  
    //该方法用于从XML配置文件中提取图表类型,并返回类型名  
    public static String getChartType() {  
        try {  
            //创建文档对象  
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();  
            DocumentBuilder builder = dFactory.newDocumentBuilder();  
            Document doc;                             
            doc = builder.parse(new File("config.xml"));   
          
            //获取包含图表类型的文本节点  
            NodeList nl = doc.getElementsByTagName("chartType");  
            Node classNode = nl.item(0).getFirstChild();  
            String chartType = classNode.getNodeValue().trim();  
            return chartType;  
        }     
        catch(Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  

class Client {  
    public static void main(String args[]) {  
        Chart chart;  
        String type = XMLUtil.getChartType(); //读取配置文件中的参数  
        chart = ChartFactory.getChart(type); //创建产品对象  
        chart.display();  
    }  
}  

总结

适用场景

   (1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
   (2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

思考

  在简单工厂模式中增加新的具体产品时是否符合“开闭原则”?如果不符合,原有系统需作出哪些修改?
【答案】工厂方法模式

上一篇 下一篇

猜你喜欢

热点阅读