工厂三兄弟之简单工厂模式
模式定义:
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的 实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是 静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属 于类创建型模式。
多读几遍定义 然后我们来看下它的结构:
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需 要的对象,而无须知道其创建细节。
来来来 举个栗子:
Sunny软件公司欲基于Java语言开发一套图表库,该图表库可以为应用系统提供各种不同外观 的图表,例如柱状图、饼状图、折线图等。Sunny软件公司图表库设计人员希望为应用系统开 发人员提供一套灵活易用的图表库,而且可以较为方便地对图表库进行扩展,以便能够在将 来增加一些新类型的图表。
然后程序员出了个设计方案,并将图表的实现代码封装在Chart类中:
/** * description: * Created by yueli * on 2019/7/9 */ public 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")) { //显示折线图 } } } |
客户端代码通过调用Chart类的构造函数来创建图表对象,根据参数type的不同可以得到不同 类型的图表,然后再调用display()方法来显示相应的图表。
不难看出,Chart类是一个“巨大的”类,在该类的设计中存在如下几个问题:
(1) 在Chart类中包含很多“if…else…”代码块,整个类的代码相当冗长,代码越长,阅读难度、 维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过 程中需要做大量的条件判断。
(2) Chart类的职责过重,它负责初始化和显示所有的图表对象,将各种图表对象的初始化代码 和显示代码集中在一个类中实现,违反了“单一职责原则”,不利于类的重用和维护;而且将大 量的对象初始化代码都写在构造函数中将导致构造函数非常庞大,对象在创建时需要进行条 件判断,降低了对象创建的效率。
(3) 当需要增加新类型的图表时,必须修改Chart类的源代码,违反了“开闭原则”。
(4) 客户端只能通过new关键字来直接创建Chart对象,Chart类与客户端类耦合度较高,对象的 创建和使用无法分离。
(5) 客户端在创建Chart对象之前可能还需要进行大量初始化设置,例如设置柱状图的颜色、高 度等,如果在Chart类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设 置,这些代码在每次创建Chart对象时都会出现,导致代码的重复。
面对这种如此巨大、职责如此重,且与客户端代码耦合度非常高的类,我们应该怎么办?
先上代码 看看我们用简单工厂模式改造后的效果:
/** * description: * Created by yueli * on 2019/7/9 */ //抽象图表接口:抽象产品类 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 = ChartFactory.getChart("pie");
但是还是有改动,不符合开闭原则了,我要换图表还是要改客户端的代码。这时候我们的解决办法是将静态工厂方法的参数存储在properties中然后在程序中引用它:
String type = XMLUtil.getChartType(); //读取配置文件中的参数
chart = ChartFactory.getChart(type); //创建产品对象
至此简单工厂模式的设计demo就讲完了。
下面我们分析下这种模式:
简单工厂模式的优点:
1.客户端免除了创建产品对象的职责,仅仅是“消费“产品,实现了对象和使用的分离;
2.客户端无需知道具体产品类的类名,只需知道对应产品的对应入参,减少记忆量;
3.通过引入配置文件可以不修改客户端的情况下更换产品,提高灵活性;
缺点:
1.工厂类集中了所有产品的创建逻辑,职责过重;
2.式势必会增加系统中类的个数,增加了系统的复杂 度和理解难度;
3. 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成 工厂逻辑过于复杂,不利于系统的扩展和维护。
4.由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
适用场景:
(1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太 过复杂。
(2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
来来思考下为啥静态?
static方法可以通过类名访问,也可以通过类的实例访问。
static方法不能访问类中非static的数据。
比如
class A { static void F(){} };
在main函数中可以
A a;
a.F();
也可以 A.F();
普通方法又叫实例方法,只能通过类的实例访问。
他只能a.F();
一个JAVA类被加载的顺序:
1.加载静态成员、代码块
2.加载非静态成员、代码块
3.调用构造方法。
其实不用静态方法也可以,只是用静态方法之后,就不用初始化工厂而直接得到产品。