目录
概念
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
我们常见的工厂模式大致分为三种:简单工厂、工厂方法和抽象工厂,我们比较常用的是简单工厂与工厂方法,抽象工厂顾名思义,他比较抽象,在项目中使用场景也比较有限,他是为了解决简单工厂与工厂方法解决不了的更复杂的对象创建。
不过我们只要掌握了简单工厂与工厂方法就可以了,因为项目中使用最多的也是这两种,现在我们来看第一种:简单工厂
简单工厂
需求:短信验证码发送功能,现在有四种情况需要发送短信验证码,1:用户注册;2:找回密码;3:修改交易密码;4:提现,针对每种情况现需要判断的条件都不相同。
1.用户注册:需要判断手机号是否注册过,注册过的不予通过。
2.找回密码:判断手机号是否注册过,没有注册过的不予通过。
3.修改交易密码:判断用户是否被禁用
4.提现:判断账户余额是否大于提现余额
由于这四种情况的判断条件都不一致,我们是不是针对四种不同的情况提供4个接口呢?肯定是不需要的,不知道Java有一个很厉害的属性,那就是多态,这里可以很轻松的使用多态解决问题。
上代码:
/**
* 发送短信验证码
* @param phone 手机号
* @param type 类型 1:用户注册 2:找回密码 3:修改支付密码 4:提现
* @return
*/
@GetMapping(value = "/sendSms")
public String sendSms(@RequestParam("phone") String phone, @RequestParam("type") Integer type){
SmsService smsService = null ;
if(type == 1){
smsService = new RegisterSereviceImpl();
}else if(type == 2){
smsService = new ForgetPwdServiceImpl();
}else if(type == 3){
smsService = new PayPwdServiceImpl();
}else if(type == 4){
smsService = new WithdrawSereviceImpl();
}else{
throw new RuntimeException("没有找到指定的短信验证码类型");
}
//check方法的返回值用boolean接受是不合理的,因为你要告诉用户为什么发送验证码失败,
// 我这里只是案例展示,所以自己实践的时候最好把提示消息带上
boolean flag = smsService.check(phone);
if(!flag){
log.info("不符合要求,发送失败");
return "短信验证码发送失败";
}
//校验通过,下面就是发送验证码了,你可以通过异步的方式发送,比如:Mq(推荐这种),也可以同步
log.info("发送短信验证码");
return "发送短信验证码成功";
}
/**
* 短信验证码处理
*/
public interface SmsService {
/**
* 校验手机号码
* @param phone
* @return
*/
boolean check(String phone);
}
/**
* 忘记密码的逻辑处理
*/
@Service
@Slf4j
public class ForgetPwdServiceImpl implements SmsService {
@Override
public boolean check(String phone) {
log.info("这里是忘记密码的逻辑处理,判断用户是否注册过,没有注册过的返回false");
return true;
}
}
/**
* 支付密码的逻辑处理
*/
@Service
@Slf4j
public class PayPwdServiceImpl implements SmsService {
@Override
public boolean check(String phone) {
log.info("这里是发送修改支付密码的短信验证码逻辑处理,判断用户是否被禁用,禁用返回false");
return true;
}
}
/**
* 注册逻辑处理
*/
@Service
@Slf4j
public class RegisterSereviceImpl implements SmsService {
@Override
public boolean check(String phone) {
log.info("这里是发送注册用户的短信验证码逻辑处理,判断用户是否注册过,注册过返回false");
return true;
}
}
@Service
@Slf4j
public class WithdrawSereviceImpl implements SmsService {
@Override
public boolean check(String phone) {
log.info("这里是发送用户提现的短信验证码逻辑处理,判断用户余额是否足够,不足返回false");
return true;
}
}
这就是利用多态的特性解决了需要创建多个controller问题,但依旧还是留下了一个比较不爽的问题,那就是在接口层有着一堆的if/else判断,看着让人十分恼火,我们试着重构于一下,根据类/方法的单一原则,将获取实例的那一步抽离出来,与业务方法分离,将创建实例放到一个单独的类中,这个类就是我们接着要讲的简单工厂。
第一种实现
改造代码开始
/**
* 发送短信验证码
* @param phone 手机号
* @param type 类型 1:用户注册 2:找回密码 3:修改支付密码 4:提现
* @return
*/
@GetMapping(value = "/sendSms")
public String sendSms(@RequestParam("phone") String phone, @RequestParam("type") Integer type){
SmsService smsService = SmsCheckConfigFactory.createSmsInstance(type);
//check方法的返回值用boolean接受是不合理的,因为你要告诉用户为什么发送验证码失败,
// 我这里只是案例展示,所以自己实践的时候最好把提示消息带上
boolean flag = smsService.check(phone);
if(!flag){
log.info("不符合要求,发送失败");
return "短信验证码发送失败";
}
//校验通过,下面就是发送验证码了,你可以通过异步的方式发送,比如:Mq(推荐这种),也可以同步
log.info("发送短信验证码");
return "发送短信验证码成功";
}
/**
* 工厂类
*/
public class SmsCheckConfigFactory {
/**
* 创建实例
* @param type 类型 1:用户注册 2:找回密码 3:修改支付密码 4:提现
* @return
*/
public static SmsService createSmsInstance(Integer type ){
SmsService smsService = null ;
if(type == 1){
smsService = new RegisterSereviceImpl();
}else if(type == 2){
smsService = new ForgetPwdServiceImpl();
}else if(type == 3){
smsService = new PayPwdServiceImpl();
}else if(type == 4){
smsService = new WithdrawSereviceImpl();
}else{
throw new RuntimeException("没有找到指定的短信验证码类型");
}
return smsService;
}
}
改造完成,新建了一个SmsCheckConfigFactory工厂类,实例的创建由controller转移到SmsCheckConfigFactory工厂类,这就是简单工厂,简单工厂的实现很简单,也很容易理解,就是将实例的创建转交给工厂类,但是还是有着一个问题我们没有解决,那就是之前说的if/else判断还是没有得到解决,我们接着往下看。
第二种实现
我们之前创建实例都是在调用的时候,其实我们也可以在程序启动的时候将实例创建在内存,调用的时候直接在内存中返回给调用者即可,所以我们继续改造一下:
/**
* 工厂类
*/
public class SmsCheckConfigFactory {
private static final Map<Integer,SmsService> smsInstance = new HashMap<Integer, SmsService>();
//初始化
static {
smsInstance.put(1,new RegisterSereviceImpl());
smsInstance.put(2,new ForgetPwdServiceImpl());
smsInstance.put(3,new PayPwdServiceImpl());
smsInstance.put(4,new WithdrawSereviceImpl());
}
/**
* 创建实例
* @param type 类型 1:用户注册 2:找回密码 3:修改支付密码 4:提现
* @return
*/
public static SmsService createSmsInstance(Integer type ){
if(type == null ){
return null;
}
return smsInstance.get(type);
}
}
这是两种简单工厂的实现方式,可以根据自己的需求进行改造。
工厂方法
在简单工厂中我们还是遗留着if/else的坑,一看到过多的if/else,我们的第一想法就是利用多态来消除它,接着改造代码:
public interface ISmsCheckConfigFactory {
/**
* 创建实例
* @return
*/
SmsService createSmsInstance();
}
public class ForgetPwdServiceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new ForgetPwdServiceImpl();
}
}
public class PayPwdServiceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new PayPwdServiceImpl();
}
}
public class RegisterSereviceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new RegisterSereviceImpl();
}
}
public class WithdrawSereviceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new WithdrawSereviceImpl();
}
}
利用多态解决了创建实例的if/else,但是又留下了一个问题,那就是工厂实例的获取,我们调用工厂实例的时候又需要if/else,难道我们做的这么多改造都是不用功吗?很明显不是的,如果你们上面的简单工厂看的够仔细的话,可能你已经知道怎么解决这个问题了,没错,那就是给创建实例的工厂也分配一个工厂,工厂的工厂,这样就完美的消除了if/else,既然想到了方法,那我们动手。
/**
* 工厂的工厂类
*/
public class SmsCheckConfigFactoryMap {
private static final Map<Integer,ISmsCheckConfigFactory> smsInstance = new HashMap<Integer, ISmsCheckConfigFactory>();
//初始化
static {
smsInstance.put(1,new RegisterSereviceFactory());
smsInstance.put(2,new ForgetPwdServiceFactory());
smsInstance.put(3,new PayPwdServiceFactory());
smsInstance.put(4,new WithdrawSereviceFactory());
}
/**
* 创建实例
* @param type 类型 1:用户注册 2:找回密码 3:修改支付密码 4:提现
* @return
*/
public static ISmsCheckConfigFactory createSmsInstance(Integer type ){
if(type == null ){
return null;
}
return smsInstance.get(type);
}
这样,我们就完全消除了if/else语句,如果后续开发中我们需要添加新的类型,比如:修改登录密码、转账等等,我们只需要在ISmsCheckConfigFactory接口上做扩展,扩展性得到了提升,代码逼格也提升了不少,当然了,如果只有两三个类型,没有后续的扩展,其实使用if/else也还是不错的,因为使用工厂会导致维护的成本加高,因为会产生额外的类,具体使用情况还是得结合着需求出发,不能一味的追求消灭if/else。
抽象工厂
抽象工厂模式(Abstract Factory Pattern)隶属于设计模式中的创建型模式,用于产品族的构建。抽象工厂是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂是指当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。
工厂模式中的每一个形态都是针对一定问题的解决方案,工厂方法针对的是多个产品系列结构;而抽象工厂模式针对的是多个产品族结构,一个产品族内有多个产品系列
需求,之前我们发送验证码是通过类型来做判断,现在需求变更了,还有一种判断方式,那就是用户的类型,比如:管理员/普通用户。
那这时候我们怎么改造代码呢?
/**
* 身份类型校验短信验证码
*/
public interface SmsUserTypeService {
/**
* 根据手机号校验
* @param phone
* @return
*/
boolean chech(String phone);
}
public class SysForgetPwdServiceImpl implements SmsUserTypeService {
@Override
public boolean chech(String phone) {
return false;
}
}
public class SysPayPwdServiceImpl implements SmsUserTypeService {
@Override
public boolean chech(String phone) {
return false;
}
}
public class SysRegisterSereviceImpl implements SmsUserTypeService {
@Override
public boolean chech(String phone) {
return false;
}
}
public class SysWithdrawSereviceImpl implements SmsUserTypeService {
@Override
public boolean chech(String phone) {
return false;
}
}
/**
* 工厂类
*/
public interface ISmsCheckConfigFactory {
/**
* 创建实例
* @return
*/
SmsService createSmsInstance();
/**
* 根据用户类型来判断发送短信验证码
* @return
*/
SmsUserTypeService createSmsUserTypeInstance();
}
public class ForgetPwdServiceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new ForgetPwdServiceImpl();
}
@Override
public SmsUserTypeService createSmsUserTypeInstance() {
return new SysForgetPwdServiceImpl();
}
}
public class PayPwdServiceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new PayPwdServiceImpl();
}
@Override
public SmsUserTypeService createSmsUserTypeInstance() {
return new SysPayPwdServiceImpl();
}
}
public class RegisterSereviceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new RegisterSereviceImpl();
}
@Override
public SmsUserTypeService createSmsUserTypeInstance() {
return new SysRegisterSereviceImpl();
}
}
public class WithdrawSereviceFactory implements ISmsCheckConfigFactory {
@Override
public SmsService createSmsInstance() {
return new WithdrawSereviceImpl();
}
@Override
public SmsUserTypeService createSmsUserTypeInstance() {
return new SysWithdrawSereviceImpl();
}
}
改造完成,代码又增加了一堆,但是看起来还是满爽的,上面的这种模式就是抽象工厂模式,使用场景比较特殊,项目中很少使用,所以做个了解就可以了。
总结
其实上面的几种工厂都是为了抽离方法,提高代码可扩展性,在日常开发中,并一定要严格的按着这个要求,代码最重要的一点莫过于可读性,就算你的代码写得很漂亮,但是你的同事看起来很吃力,那么在团队开发过程中就会发生各种各样的问题,所以,在保证代码可读性的情况下,尽量提高代码的可扩展性、可维护性才是王道。