1.问 : 很多时候通过反射机制就可以很灵活地创建对象,为毛还要工厂?
将对象的创建和使用分开,单一职责。两个类A和B之间的关系应该仅仅是A创建B或是A使用B,而不能两种关系都有。
与一个对象相关的职责通常有三类:对象本身所具有的职责、创建对象的职责和使用对象的职责
在Java语言中,我们通常有以下几种创建对象的方式:
(1) 使用new关键字直接创建对象;
(2) 通过反射机制创建对象;
(3) 通过clone()方法创建对象;
(4) 通过工厂类创建对象。
new 灵活性不好,不符合开闭原则。
反射 不太安全,而且性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。在业务代码中唯一合理(直接)使用反射的场景是通过AOP。除此之外,你最好远离反射这一特性。
将对象的创建和使用分离还有一个好处:防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中
一个类可能拥有多个构造函数,构造函数名字都与类名相同,将对象的创建过程封装在工厂类中,我们可以提供一系列名字完全不同的工厂方法,每一个工厂方法对应一个构造函数,客户端可以以一种更加可读、易懂的方式来创建对象。
2.简单工厂模式:一个工厂类 来创建所有的产品对象
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();
}
}
我们可以将静态工厂方法的参数存储在XML或properties格式的配置文件中,如下config.xml所
示:
<?xml version="1.0"?>
<config>
<chartType>histogram</chartType>
</config>
再通过一个工具类XMLUtil来读取配置文件中的字符串参数,XMLUtil类的代码如下所示:
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;
}
}
}
有时候,为了简化简单工厂模式,我们可以将抽象产品类和工厂类合并,将静态工厂方法移至抽象产品类中。
缺点:
(1) 工厂类过于庞大,包含了大量的if…else…代码,导致维护和测试难度增大;
(2) 系统扩展不灵活,如果增加新类型的产品类,必须修改静态工厂方法的业务逻辑,违反了“开闭原则”。
适用场景
(1) 工厂类负责创建的对象比较少,工厂方法中的业务逻辑不会太过复杂。
(2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
结论:对象种类比较少的时候用。
3.工厂方法:针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构
//*****引入抽象工厂
interface Factory { public Product factoryMethod(); }w class ConcreteFactory implements Factory { public Product factoryMethod() { return new ConcreteProduct(); } Factory factory; factory = new ConcreteFactory(); //可通过配置文件实现 Product product; product = factory.factoryMethod();}
缺点:类太多
4.抽象工厂模式:在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如图5所示:
abstract class AbstractFactory { public abstract AbstractProductA createProductA(); //工厂方法一 public abstract AbstractProductB createProductB(); //工厂方法二 …… } class ConcreteFactory1 extends AbstractFactory { //工厂方法一 public AbstractProductA createProductA() { return new ConcreteProductA1(); } //工厂方法二 public AbstractProductB createProductB() { return new ConcreteProductB1(); } …… }
按钮 文本框 组合框
绿色
蓝色
//界面皮肤工厂接口:抽象工厂 interface SkinFactory { public Button createButton(); public TextField createTextField(); public ComboBox createComboBox(); } //Spring皮肤工厂:具体工厂 class SpringSkinFactory implements SkinFactory { public Button createButton() { return new SpringButton(); } public TextField createTextField() { return new SpringTextField(); } public ComboBox createComboBox() { return new SpringComboBox(); } } //Summer皮肤工厂:具体工厂 class SummerSkinFactory implements SkinFactory { public Button createButton() { return new SummerButton(); } public TextField createTextField() { return new SummerTextField(); } public ComboBox createComboBox() { return new SummerComboBox(); } }
一般情况下,一个具体工厂中只有一个或者一组重载的工厂方法。但是有时候我们希望一个工厂可以提供多个产品对象,而不是单一的产品对象,如一个电器工厂,它可以生产电视机、电冰箱、空调等多种电器。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。
总结:增加产品族支持开闭原则,增加产品等级,则不支持。(产品等级:父类与多个子类实现)开闭原则倾斜性。
适用场景
在以下情况下可以考虑使用抽象工厂模式:
(1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
(2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
(3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。
(4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。