一、工厂模式
在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替
>
注意:上述复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。
工厂模式定义 :定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
按照实际业务场景划分,工厂模式分为三种:
- 简单工厂模式,在简单工厂模式中创建实例的方法通常为静态方法,因此简单工厂模式又叫作静态工厂方法模式。
- 工厂方法模式
- 抽象工厂模式
>
“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
1. 简单工厂模式
1.1 简单工厂模式的优缺点
优点
- 工厂类包含必要的逻辑判断,可以决定什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
- 客户端无需知道所创建的具体产品的类名,只需要知道参数即可
- 也可以引入配置文件,在不修改客户端代码的情况下增加新的具体产品类
缺点
- 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受到影响。且工厂类的代码非常臃肿,违背高聚合的原则。
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
- 系统扩展困难,一旦增加新的产品,不得不修改工厂逻辑,在产品较多的时候,可能造成逻辑过于复杂。
- 简单工厂模式使用了静态static工厂方法,造成工厂角色无法形成基于集成的等级结构。
应用场景
对于产品种类相对较少,考虑使用简单工厂模式,使简单工厂模式的客户端传入相应的工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便的创建所需产品。
1.2 模式的结构与实现
简单工厂模式的主要角色如下:
- 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
- 抽象产品(AbstractProduct):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
- 具体产品(ConcreteProduct):是简单工厂模式的创建目标。
直接上代码看简单工厂的实现
/**
* 工厂类
*/
public class SimpleFactory {
public static Product makeProduct(int kind) {
switch (kind) {
case Const.PRODUCT_A:
return new Product1();
case Const.PRODUCT_B:
return new Product2();
}
return null;
}
}
/**
* 抽象产品接口
*/
public interface Product {
// 产品的展示
void show();
}
/**
* 实际的产品1
*/
public class Product1 implements Product {
@Override
public void show() {
System.out.println("具体产品1显示...");
}
}
/**
* 实际的产品2
*/
public class Product2 implements Product {
@Override
public void show() {
System.out.println("具体产品2显示...");
}
}
public class Const {
public static final int PRODUCT_A = 0;
public static final int PRODUCT_B = 1;
}
public class Test {
/**
* @Author liyuan
* @Description 简单工厂模式的核心要素
* 1. 工厂类:SimpleFactory
* 2. 产品抽象接口: Product
* 3. 实际要生产的产品 Product1 Product2 Product3.....
* @Date 9:42 2021-5-27
* @Param
* @param args
* @return
**/
public static void main(String[] args) {
// 简单工厂生成产品的方式
System.out.println(SimpleFactory.makeProduct(Const.PRODUCT_A));
}
}
很明显,简单工厂隐藏了产品的创建过程,提供了统一的根据参数来获取不同的产品的方法,但是有个很大的缺点就是,新增产品就需要修改工厂类。
2. 工厂方法模式
2.1 工厂方法模式的优缺点
优点
- 用户只需要知道工厂的名称就可以得到工厂的产品,无需关心产品的具体创建的过程
- 灵活性增强,对于产品的创建,只需要多谢一个工厂类
- 典型的解耦架构,高层模块只需要知道产品的抽象类,无需关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则
缺点
- 类的个数容易多,增加复杂度
- 增加了系统的抽象性和理解难度
- 抽象产品只能生产一种产品,此弊端可以用抽象工厂模式解决。
应用场景
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌
2.2 模式的结构与实现
工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。
工厂方法模式主要的角色如下:
- 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法,newProduct()来创建产品。
- 具体工厂:主要是实现了抽象工厂中的抽象方法,完成具体的产品创建过程。
- 抽象产品:定义了产品的规范,描述了产品的主要特征和功能。
- 具体产品:实现了抽象产品角色所定义的接口,具体由工厂来创建,它和工厂一一对应。
工厂方法模式的实现
/**
* 抽象工厂类,抽象生产产品的方法
*/
public interface AbstractFactory {
Product newProduct();
}
/**
* 抽象产品
**/
public interface Product {
public void show();
}
/**
* 具体产品的工厂1
**/
public class ConcreateFactory1 implements AbstractFactory {
@Override
public Product newProduct() {
System.out.println("具体工厂1生成-->具体产品1...");
return new Product1();
}
}
/**
* 具体产品的工厂2
**/
public class ConcreateFactory2 implements AbstractFactory {
@Override
public Product newProduct() {
System.out.println("具体工厂2生成-->具体产品2...");
return new Product2();
}
}
/**
* 具体产品1
*/
public class Product1 implements Product {
@Override
public void show() {
System.out.println("具体产品1显示...");
}
}
/**
* 具体产品2
*/
public class Product2 implements Product {
@Override
public void show() {
System.out.println("具体产品2显示...");
}
}
/**
* 生产产品
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Product a ;
AbstractFactory af;
af = (AbstractFactory) ReadXML1.getObject();
a = af.newProduct();
a.show();
}
}
class ReadXML1 {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getObject() {
try {
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("E:\\config1.xml"));
//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = "com.liyuan.designMode.factory.factoryMethod.instance."+classNode.getNodeValue();
//System.out.println("新类名:"+cName);
//通过类名生成实例对象并将其返回
Class<?> c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
config1.xml如下
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>ConcreateFactory1</className>
</config>
写到这里很明显这种工厂模式有极大的缺点,一个工厂类只能生成一种产品,如果需要多个产品的话,就会极大的增加类的个数。
3. 抽象工厂模式
3.1 抽象工厂模式的定义与特点
抽象工厂模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
抽象工厂模式的使用需要满足以下的条件:
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下:
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
3.2 模式的结构与实现
抽象工厂模式的主要角色如下。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreateProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
直接上个例子
【例】用抽象工厂模式设计农场类。
分析:农场中除了像畜牧场一样可以养动物,还可以培养植物,如养马、养牛、种菜、种水果等,所以本实例的畜牧场类复杂,必须用抽象工厂模式来实现。本例用抽象工厂模式来设计两个农场,一个是北京农场用于养牛和种菜,一个是山西农场用于养马和种水果,可以在以上两个农场中定义一个生成动物的方法 newAnimal() 和一个培养植物的方法 newPlant()。
程序代码实现如下:
/**
* @Author liyuan
* 抽象工厂: 农场
**/
public interface FarmAbstractFactory {
public Animal newAnimal();
public Plant newPlant();
}
/**
* @Author liyuan
* 抽象产品: 动物类
**/
public interface Animal {
public void show();
}
/**
* @Author liyuan
* 抽象产品: 植物类
**/
public interface Plant {
public void show();
}
/**
* @author liyuan
* @date 2021年05月27日 15:00
* 具体产品:马
*/
public class Horse implements Animal {
private String name;
private Integer age;
public Horse(){
name = "马";
age = 5 ;
}
@Override
public String toString() {
return "Horse{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public void show() {
System.out.println(this.toString());
}
}
/**
* @author liyuan
* @date 2021年05月27日 15:05
* * 具体产品:牛
*/
public class Cattle implements Animal {
private String name ;
private Integer age ;
public Cattle() {
this.name = "老黄牛";
this.age =12 ;
}
@Override
public String toString() {
return "Cattle{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public void show() {
System.out.println(this.toString());
}
}
/**
* @author liyuan
* @date 2021年05月27日 15:07
* 具体产品:水果
*/
public class Fruitage implements Plant {
private String size ;
public Fruitage() {
size = "小";
}
@Override
public String toString() {
return "Fruitage{" +
"size='" + size + '\'' +
'}';
}
@Override
public void show() {
System.out.println(this.toString());
}
}
/**
* @author liyuan
* @date 2021年05月27日 15:08
* 具体产品:蔬菜
*/
public class Vegetables implements Plant {
private String size;
public Vegetables (){
size ="中";
}
@Override
public String toString() {
return "Vegetables{" +
"size='" + size + '\'' +
'}';
}
@Override
public void show() {
System.out.println(this.toString());
}
}
/**
* @author liyuan
* @date 2021年05月27日 15:21
* 具体工厂:山西农场
*/
public class SXform implements FarmAbstractFactory {
@Override
public Animal newAnimal() {
return new Cattle();
}
@Override
public Plant newPlant() {
return new Vegetables();
}
}
/**
* @author liyuan
* @date 2021年05月27日 15:19
* 具体的工厂类:北京农场
*/
public class BJfarm implements FarmAbstractFactory {
@Override
public Animal newAnimal() {
return new Horse();
}
@Override
public Plant newPlant() {
return new Fruitage();
}
}
public class Test {
public static void main(String[] args) {
try {
FarmAbstractFactory f;
Animal a;
Plant p;
f = (FarmAbstractFactory) Class.forName("com.liyuan.designMode.factory.abstractfactory.example.concreate.SXform").newInstance();
a = f.newAnimal();
p = f.newPlant();
a.show();
p.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}