一、什么是工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
工厂模式的好处是这些对象不需要暴露自身的创建过程,统一由工厂模式进行创建和提供,隐藏了创建细节,避免了错误的创建对象的形式,也减少了重复创建冗余代码,并且是通过使用一个共同的接口来指向新创建的对象。
一般情况下,工厂模式可以细分为三类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
简单工厂模式只是工厂方法模式的一个特例而已。
所以,从权威的角度说,工厂模式只分为:工厂模式 和 抽象工厂模式 两大类。
1、工厂模式介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
2、抽象工厂介绍
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。
注意事项:产品族难扩展,产品等级易扩展。
二、本文案例通用类
1、新建客户基类
可以把所有客户公共的信息放到一个客户基类中,比如:客户名、客户类型等,所有的客户继承这个抽象基类。
/**
* 客户基类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class Customer {
/**
* 客户名称
*/
private String name;
/**
* 客户类型
*/
private CustomerType customerType;
}
相应的枚举类
/**
* 客户类型
*/
public enum CustomerType {
//电商客户类
Merchant,
//银行客户类
BankPartner,
//代理商户类
Agent;
}
2、新建具体的客户类
电商商户类:
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) // 该参数为true时,可以打印继承自父类的属性
public class Merchant extends Customer {
/**
* 合同类型
*/
private int contractType;
/**
* 结算周期(天)
*/
private int settmentDays;
public Merchant(String name, String type) {
// 调用父类的构造方法
super(name, type);
}
}
银行客户类:
/**
* 银行客户类
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class BankPartner extends Customer {
/**
* 银行编码
*/
private String code;
/**
* 银行地址
*/
private String address;
public BankPartner(String name, CustomerType customerType) {
super(name, customerType);
}
}
代理商类
/**
* 代理商类
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class Agent extends Customer {
/**
* 代理周期
*/
private int period;
/**
* 代理产品
*/
private int[] products;
public Agent(String name, CustomerType customerType) {
super(name, customerType);
}
}
二、简单工厂模式
比如 XX 公司是做支付的,公司有几大类的客户:电商商户、银行客户、代理商……
创建这些客户的时候我们可以用简单工厂模式来实现看看。
1、新建简单工厂类
新建一个简单工厂,提供一个公共静态方法,根据不同的客户类型创建不同的客户。
/**
* 简单工厂类
*/
public class CustomerFactory {
private static Merchant createMerchant(String type, CustomerType customerType) {
return new Merchant(type, customerType);
}
private static BankPartner createBankPartner(String type, CustomerType customerType) {
return new BankPartner(type, customerType);
}
private static Agent createAgent(String type, CustomerType customerType) {
return new Agent(type, customerType);
}
// 静态方法,用于创建指定对象
public static Customer create(String name, CustomerType customerType) {
if (CustomerType.Merchant.equals(customerType)) {
return createMerchant(name, customerType);
} else if (CustomerType.BankPartner.equals(customerType)) {
return createBankPartner(name, customerType);
} else if (CustomerType.Agent.equals(customerType)) {
return createAgent(name, customerType);
}
return null;
}
}
2、测试
// 创建商户类
Merchant merchant = (Merchant)CustomerFactory.create("张三", CustomerType.Merchant);
System.out.println(merchant);
// 创建银行客户类
BankPartner bankPartner = (BankPartner) CustomerFactory.create("李四", CustomerType.BankPartner);
System.out.println(bankPartner);
// 创建代理商类
Agent agent = (Agent)CustomerFactory.create("王五", CustomerType.Agent);
System.out.println(agent);
3、模式优缺点
可以看出简单工厂的使用很简单,就是耦合性太高了。
第一,对象和基类之间是基于继承的。
第二,工厂类耦合了不同对象的创建,如果对象类型不是固定或者经常变动的,就要频繁修改工厂类,比如我现在要再加一种客户,就必须要改动工厂类,不符开闭原则。
所以,简单工厂只适用于固定类型对象的创建。
三、工厂方法模式
工厂方法就是为某类产品提供一个工厂接口,然后为每个产品提供一个工厂实现类。
1、新建工厂方法接口
/**
* 工厂方法客户接口
*/
public interface CustomerFactory {
Customer create(String name, CustomerType customerType);
}
2、新建工厂实现类
/**
* 商户工厂实现类
*/
public class MerchantFactory implements CustomerFactory {
@Override
public Customer create(String name, CustomerType customerType) {
return new Merchant(name, customerType);
}
}
/**
* 银行客户工厂
*/
public class BankPartnerFactory implements CustomerFactory {
@Override
public Customer create(String name, CustomerType customerType) {
return new BankPartner(name, customerType);
}
}
/**
* 代理商工厂实现类
*/
public class AgentFactory implements CustomerFactory {
@Override
public Customer create(String name, CustomerType customerType) {
return new Agent(name, customerType);
}
}
3、测试
CustomerFactory merchantFactory = new MerchantFactory();
Customer merchant = merchantFactory.create("张三", CustomerType.Merchant);
System.out.println(merchant);
CustomerFactory bankPartnerFactory = new BankPartnerFactory();
Customer bankPartner = bankPartnerFactory.create("李四", CustomerType.BankPartner);
System.out.println(bankPartner);
CustomerFactory agentFactory = new AgentFactory();
Customer agent = agentFactory.create("王五", CustomerType.Agent);
System.out.println(agent);
4、模式优缺点
可以看出,工厂方法也是挺简单易用的,耦合性问题也解决了,每增加一个产品就新增一个产品工厂实现类就行了,扩展性非常好。
但也有一个问题,如果产品非常多,那势必会造成工厂实现类泛滥,另外一种可怕的场景就是,如果涉及到工厂接口变更,工厂实现类的维护简直就是一种恶梦。
四、抽象工厂模式
工厂方法中一个工厂只能创建一种对象,如果现在每次创建客户的时候都需要同时创建一份客户扩展资料,那就可以考虑使用抽象工厂。
1、抽象工厂客户接口
/**
* 抽象工厂客户接口
*/
public interface CustomerFactory {
Customer createCustomer(String name, CustomerType customerType);
CustomerExt createCustomerExt();
}
2、抽象工厂客户实现类
/**
* 商户工厂
*/
public class MerchantFactory implements CustomerFactory {
@Override
public Customer createCustomer(String name, CustomerType customerType) {
return new Merchant(name, customerType);
}
@Override
public CustomerExt createCustomerExt() {
return new MerchantExt();
}
}
/**
* 银行客户工厂
*/
public class BankPartnerFactory implements CustomerFactory {
@Override
public Customer createCustomer(String name, CustomerType customerType) {
return new BankPartner(name, customerType);
}
@Override
public CustomerExt createCustomerExt() {
return new BankPartnerExt();
}
}
/**
* 代理商工厂
*/
public class AgentFactory implements CustomerFactory {
@Override
public Customer createCustomer(String name, CustomerType customerType) {
return new Agent(name, customerType);
}
@Override
public CustomerExt createCustomerExt() {
return new AgentExt();
}
}
3、客户扩展基类
可以把所有客户公共的扩展信息放到一个客户扩展基类中,比如:客户曾用名、客户扩展说明等,所有的客户继承这个扩展抽象基类。
/**
* 客户扩展基类
*/
@Data
@NoArgsConstructor
public abstract class CustomerExt {
/**
* 客户曾用名
*/
private String formerName;
/**
* 客户扩展说明
*/
private String note;
}
4、客户扩展子类
/**
* 商户扩展
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class MerchantExt extends CustomerExt {
/**
* 介绍人
*/
private int introduceName;
/**
* 介绍人电话
*/
private String introduceTel;
}
/**
* 银行客户扩展
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class BankPartnerExt extends CustomerExt {
/**
* 分行个数
*/
private int branchCount;
/**
* ATM个数
*/
private int atmCount;
}
/**
* 商户
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class AgentExt extends CustomerExt {
/**
* 来源
*/
private String source;
/**
* 资质
*/
private String certification;
}
5、测试
CustomerFactory merchantFactory = new MerchantFactory();
Customer merchant = merchantFactory.createCustomer("张三", CustomerType.Merchant);
CustomerExt merchantExt = merchantFactory.createCustomerExt();
System.out.println(merchant);
System.out.println(merchantExt);
CustomerFactory bankPartnerFactory = new BankPartnerFactory();
Customer bankPartner = bankPartnerFactory.createCustomer("李四", CustomerType.BankPartner);
CustomerExt bankPartnerExt = bankPartnerFactory.createCustomerExt();
System.out.println(bankPartner);
System.out.println(bankPartnerExt);
CustomerFactory agentFactory = new AgentFactory();
Customer agent = agentFactory.createCustomer("王五", CustomerType.Agent);
CustomerExt agentExt = agentFactory.createCustomerExt();
System.out.println(agent);
System.out.println(agentExt);
6、优缺点
可以看出,抽象工厂和工厂方法十分类似,只不过抽象工厂里面只生产一个对象,而抽象工厂可以生产多个对象。
抽象工厂缺点也很明显,第一就是和工厂方法一样工厂类非常多,第二就是扩展非常麻烦,比如我现在要为每个客户类型再加一份客户特殊资料,那所有涉及到抽象工厂的工厂类都要改,是不是要疯了。。
五、总结
如果有多个属于同一种类型的类,可以考虑使用工厂模式,统一提供生成入口,能从一定程度上解耦,扩展方便,也不用再到处 new 对象了。
但话又说回来,从示例可以看出,如果使用或者设计不当也会带来维护上的工作量。