设计模式 -- 工厂模式/抽象工厂模式

99 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

一、简介

工厂模式可以分为两种,简单工厂模式、抽象工厂模式。工厂模式也是平时开发中最常用的一种设计模式

二、简单工厂模式

基本介绍

  1. 简单工厂模式属于创建型模式,是工厂模式的一种
  2. 定义了一个创建对象的类,由某个类来封装实例化对象的行为
  3. 在软件开发中,当我们会用到大量的创建某种、某类或者某对象时,就会使用到工厂模式

优点:  1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点: 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景:  1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。\

类图

\

三、抽象工厂模式

基本介绍

1)抽象工厂模式:定义了一个接口用于创建相关或有依赖关系的对象蔟,而无需指明具体的类

2)抽象工厂模式可以将简单工厂模式和工厂方法进行整合

3)从设计层面来看,抽象工厂模式是对简单工厂模式的改进

4)将工厂抽象成两层,AbsFactory和具体实现的工厂子类,程序员可以根据创建对象类型使用对应的工厂子类,这样将单个的简单工厂变成了工厂簇

优点: 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点: 产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

使用场景:  1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。

类图

\

四、工厂模式在jdk中的应用

Calendar类

Calendar cal = Calendar.getInstance();

public static Calendar getInstance()
{
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}


private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

五、抽象工厂在实际开发中的举例

支付平台对接有时需要对接各种支付平台,那么抽象工厂就是一个很好的选择

简单看下类图

\

这里的逻辑就是一个支付抽象接口总类 AbstractPaymentGatewayService,后续根据支付总类型划分各个类型的抽象类 例如阿里支付、微信支付 对应的就是AbstractAliPaymentGatewayService、AbstractWeixinPaymentGatewayService

在向下划分即具体的支付接口

贴上代码

模块

PaymentGatewayService.class

package com.example.pay;

public interface PaymentGatewayService {

    String getChannel();

    void pay();
}

PaymentGatewayFactoryService.class

package com.example.pay;

import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class PaymentGatewayFactoryService {

    private Map<String,PaymentGatewayService> services = new ConcurrentHashMap<>();

    /***
     * @Description: 将对象注册到map
     * @param  channelId 渠道
     */
    protected void register(String channelId,PaymentGatewayService service){
        String[] gateIdArray = channelId.split(",");
        for (String gateId : gateIdArray) {
            if (null != services.get(gateId)) {
                System.out.println("error");
            }
            services.put(gateId, service);
        }
    }

    /**
     * @Description: 获取对象
     * @param gateId 渠道id
     */
    public PaymentGatewayService getPaymentGatewayService(String gateId) {
        PaymentGatewayService paymentGatewayService = services.get(gateId);
        if (null == services.get(gateId)) {
            System.out.println("error");
        }
        return paymentGatewayService;
    }
}

AbstractPaymentGatewayService.class

package com.example.pay;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

public abstract class AbstractPaymentGatewayService implements InitializingBean,PaymentGatewayService {

    @Autowired
    private PaymentGatewayFactoryService factory;

    @Override
    public void afterPropertiesSet() throws Exception {
        factory.register(this.getChannel(),this);
    }
}

AbstractAliPaymentGatewayService.class\

package com.example.pay.alipay;

import com.example.pay.AbstractPaymentGatewayService;

public abstract class AbstractAliPaymentGatewayService extends AbstractPaymentGatewayService {

    protected String host = "xxxx";
}

AliPaymentApp.class

package com.example.pay.alipay;

public class AliPaymentApp extends AbstractAliPaymentGatewayService{


    @Override
    public String getChannel() {
        return "ali-app";
    }

    @Override
    public void pay() {
        System.out.println("App pay");
    }
}

AliPaymentH5.class

package com.example.pay.alipay;


public class AliPaymentH5 extends AbstractAliPaymentGatewayService{

    @Override
    public String getChannel() {
        return "ali-h5";
    }

    @Override
    public void pay() {
        System.out.println("h5 pay");
    }
}

AbstractWeixinPaymentGatewayService.class

package com.example.pay.weixin;

import com.example.pay.AbstractPaymentGatewayService;

public abstract class AbstractWeixinPaymentGatewayService extends AbstractPaymentGatewayService {
}

WeixinJSAPIPay.class

package com.example.pay.weixin;

public class WeixinJSAPIPay extends AbstractWeixinPaymentGatewayService {
    @Override
    public String getChannel() {
        return "JS-API";
    }

    @Override
    public void pay() {
        System.out.println("jsapi pay");
    }
}

以上代码可以直接用于生产实践,具体功能需参考具体业务实现