设计模式之工厂模式
顾名思义,由工厂创建对象,返回对象给你,而你不需要关心对象的创建,你只需要获取到对象,执行与对象相关逻辑即可。
假设,需要发送邮件和发送短信,你不需要关心邮箱对象是怎么创建的,内部有什么初始化,只需要获取到邮箱对象,然后sendMail即可。
普通工厂模式
如上图的模式,CommonFactory使用produce负责生产出对应的发送设备,根据传入的参数决定是邮件发送,还是短信发送。
不需要关心MailSender和SmsSender发送需要什么初始化条件,完全由工厂去初始化。
用户只需要拿到Sender,然后调用send()方法发送对应数据即可。
发送者
/**
* @author lw
* @date 2022/3/28 0028
* @description 发送者
*/
public interface Sender {
void send(String content);
}
/**
* @author lw
* @date 2022/3/28 0028
* @description 短信发送
*/
public class SmsSender implements Sender {
//移动发送短信需要的入网条件
private String smsKey;
private String smsAccess;
public SmsSender(String smsKey,String smsAccess){
this.smsAccess = smsAccess;
this.smsKey = smsKey;
}
@Override
public void send(String content) {
System.out.println("短信发送:"+content);
}
}
/**
* @author lw
* @date 2022/3/28 0028
* @description 邮件发送
*/
public class MailSender implements Sender {
//网易发送邮件需要的条件
private String accessKey;
public MailSender(String accessKey){
this.accessKey = accessKey;
}
@Override
public void send(String content) {
System.out.println("邮件发送:"+content);
}
}
可以看出邮件发送需要发送条件,短信发送也需要发送条件。
这里把发送条件简化了,如果需要一大堆判断发送条件呢,选择运营商等等呢?难道每次创建SmsSender对象都要一大堆判断条件来初始化这个?
工厂
/**
* @author lw
* @date 2022/3/28 0028
* @description 普通工厂
*/
public class CommonFactory {
//移动发送短信需要的入网条件
private String smsKey;
private String smsAccess;
//网易发送邮件需要的条件
private String accessKey;
/**
* 生产线
* @param sendType
* @return
*/
public Sender produce(String sendType){
if("sms".equals(sendType)){
return new SmsSender(smsKey,smsAccess);
}else if("mail".equals(sendType)){
return new MailSender(accessKey);
}
return null;
}
}
使用工厂产出SmsSender和MailSender对象,不需要用户在调用时还关心这些入网条件,也不需要获取这些key什么的。完全由工厂去做。
调用
CommonFactory factory = new CommonFactory();
Sender sender = factory.produce("sms");
sender.send("hello world");
此处是直接传入字符串,当然也可以用枚举类来规范代码,防止出错。
多工厂方法模式
顾名思义,就是多个工厂方法,也就是多个方法。
为了防止传参出错,返回一个Null给调用者,那就分成多个方法来生产不同的对象。
将sms和mail的生产线拆开,分成两条生产线,一条生产SmsSender对象,一条生产MailSender对象。
/**
* 短信生产线
* @return
*/
public Sender produceSms(){
return new SmsSender(smsKey,smsAccess);
}
/**
* 邮件生产线
* @return
*/
public Sender produceMail(){
return new MailSender(accessKey);
}
调用者
MoreMethodFactory factory = new MoreMethodFactory();
Sender sender = factory.produceSms();
sender.send("hello world");
这样调用者就不需要关心传什么参数,只需要调用不同方法即可。
静态工厂模式
即将方法使用static静态化,主要是为了不需要初始化工厂对象。
不需要初始化工厂后,需要关心生产线,而不需要关心工厂初始化信息。
/**
* 生产线
* @param sendType
* @return
*/
public static Sender produce(String sendType){
if("sms".equals(sendType)){
return new SmsSender(smsKey,smsAccess);
}else if("mail".equals(sendType)){
return new MailSender(accessKey);
}
return null;
}
调用者
Sender sender = StaticFactory.produce("sms");
sender.send("hello world");
抽象工厂模式
上述工厂模式中,每加入一种发送模式都需要修改工厂信息,这里不提什么不符合开闭原则。
就说一个场景,A和B的SmsSender的初始化key不一样呢?难道说new一个Factory,然后传入初始化key?
后续又要加入produceWeixin()发送方法呢…
这样就可以产生一个如上图所示的抽象工厂Factory,这个抽象工厂就只有一个produce()方法,后续无论是SmsFactory还是MailFactory都可以实现这个抽象工厂。
/**
* @author lw
* @date 2022/3/28 0028
* @description 抽象工厂
*/
public interface Factory {
Sender produce();
}
/**
* @author lw
* @date 2022/3/28 0028
* @description 邮件工厂
*/
public class MailFactory implements Factory {
//网易发送邮件需要的条件
private String accessKey;
@Override
public Sender produce() {
return new MailSender(accessKey);
}
}
/**
* @author lw
* @date 2022/3/28 0028
* @description 短信工厂
*/
public class SmsFactory implements Factory {
//移动发送短信需要的入网条件
private String smsKey;
private String smsAccess;
@Override
public Sender produce() {
return new SmsSender(smsKey,smsAccess);
}
}
抽象工厂模式,易于后期拓展,但是每加入一种模式都需要创建一种工厂。
想象一下有30种消息发送模式,难道你需要创建30个类?
总结
以下几种场景可以考虑工厂模式:
- 需要繁杂的对象初始化;
- 多个对象有共同的功能,同时还可以抽象出一个共同的抽象对象;
至于说是用普通工厂模式,多工厂方法模式还是抽象工厂模式,这就看业务复杂度以及后续是否需要扩展等要素。
下面这种一般项目中用的多,也易于扩展,很多方法都是写在MoreMethodFactory中,等这个类复杂了就可以抽离出一些代码。
从来没有一步到位的设计,都是不断优化,不断重构。
就如同工厂一样,一开始是一个小工厂CommonFactory,生产各种东西,接到什么订单就生产什么。
等规模大了,就考虑扩展多条生产线,变成MoreMethodFactory,各个生产线负责生产不同东西。
等规模继续扩展,就变成AbstractFactory,就可以开设多个工厂,有SmsFactory和MailFactory等。